From afecaab477a284beadebc0ed1a6ca28c5a27ddec Mon Sep 17 00:00:00 2001 From: "cmanske%netscape.com" Date: Thu, 10 Feb 2000 05:14:52 +0000 Subject: [PATCH] Table editing work (bug 20973). Fixed background colorpicker (bug 21410), disable unimplemented menu items (25137), make property bundle strings more localizable (26050). r=mjudge --- editor/base/Makefile.in | 2 +- editor/base/makefile.win | 4 +- editor/base/nsEditor.cpp | 98 + editor/base/nsEditor.h | 13 + editor/base/nsEditorShell.cpp | 105 +- editor/base/nsHTMLEditor.cpp | 24 +- editor/base/nsHTMLEditor.h | 18 +- editor/base/nsHTMLEditorLog.cpp | 154 ++ editor/base/nsHTMLEditorLog.h | 12 + .../base/{EditTable.cpp => nsTableEditor.cpp} | 156 +- editor/composer/src/nsEditorShell.cpp | 105 +- editor/idl/nsIEditorShell.idl | 9 + editor/libeditor/base/nsEditor.cpp | 98 + editor/libeditor/base/nsEditor.h | 13 + editor/libeditor/html/nsHTMLEditor.cpp | 24 +- editor/libeditor/html/nsHTMLEditor.h | 18 +- editor/libeditor/html/nsHTMLEditorLog.cpp | 154 ++ editor/libeditor/html/nsHTMLEditorLog.h | 12 + editor/libeditor/html/nsTableEditor.cpp | 1649 +++++++++++++++++ editor/public/nsIHTMLEditor.h | 4 + editor/public/nsITableEditor.h | 22 +- editor/ui/composer/content/EditorCommands.js | 242 ++- editor/ui/composer/content/editor.xul | 33 +- editor/ui/composer/content/editorOverlay.xul | 82 +- editor/ui/composer/locale/en-US/editor.dtd | 5 +- .../composer/locale/en-US/editor.properties | 23 +- .../composer/locale/en-US/editorOverlay.dtd | 44 +- editor/ui/composer/skin/MANIFEST | 1 + editor/ui/composer/skin/Makefile.in | 1 + editor/ui/composer/skin/editor.css | 18 +- editor/ui/composer/skin/makefile.win | 2 + editor/ui/dialogs/content/EdDialogCommon.js | 4 +- .../ui/dialogs/content/EdNamedAnchorProps.js | 2 +- editor/ui/dialogs/content/EdPageProps.xul | 1 + 34 files changed, 2904 insertions(+), 248 deletions(-) rename editor/base/{EditTable.cpp => nsTableEditor.cpp} (93%) create mode 100644 editor/libeditor/html/nsTableEditor.cpp diff --git a/editor/base/Makefile.in b/editor/base/Makefile.in index 2541c320ae65..76120cd54874 100644 --- a/editor/base/Makefile.in +++ b/editor/base/Makefile.in @@ -45,7 +45,7 @@ CPPSRCS = \ ChangeAttributeTxn.cpp \ EditTxn.cpp \ EditAggregateTxn.cpp \ - EditTable.cpp \ + nsTableEditor.cpp \ InsertTextTxn.cpp \ PlaceholderTxn.cpp \ DeleteTextTxn.cpp \ diff --git a/editor/base/makefile.win b/editor/base/makefile.win index 661a6ad99382..8b7de432f558 100644 --- a/editor/base/makefile.win +++ b/editor/base/makefile.win @@ -52,7 +52,7 @@ CPPSRCS = \ TransactionFactory.cpp \ TypeInState.cpp \ nsHTMLEditor.cpp \ - EditTable.cpp \ + nsTableEditor.cpp \ nsInternetCiter.cpp \ nsAOLCiter.cpp \ nsInterfaceState.cpp \ @@ -88,7 +88,7 @@ CPP_OBJS = \ .\$(OBJDIR)\TransactionFactory.obj \ .\$(OBJDIR)\TypeInState.obj \ .\$(OBJDIR)\nsHTMLEditor.obj \ - .\$(OBJDIR)\EditTable.obj \ + .\$(OBJDIR)\nsTableEditor.obj \ .\$(OBJDIR)\nsInternetCiter.obj \ .\$(OBJDIR)\nsAOLCiter.obj \ .\$(OBJDIR)\nsInterfaceState.obj \ diff --git a/editor/base/nsEditor.cpp b/editor/base/nsEditor.cpp index 1d18032b55e6..6dd07be6cbcc 100644 --- a/editor/base/nsEditor.cpp +++ b/editor/base/nsEditor.cpp @@ -106,6 +106,7 @@ static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_CID(kCDOMRangeCID, NS_RANGE_CID); // transaction manager static NS_DEFINE_CID(kCTransactionManagerCID, NS_TRANSACTIONMANAGER_CID); @@ -4949,3 +4950,100 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, return result; } +nsresult +nsEditor::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset, + nsIDOMNode *aEndParent, PRInt32 aEndOffset, + nsIDOMRange **aRange) +{ + nsresult result; + result = nsComponentManager::CreateInstance(kCDOMRangeCID, nsnull, + NS_GET_IID(nsIDOMRange), + (void **)aRange); + + if (NS_FAILED(result)) + return result; + + if (!*aRange) + return NS_ERROR_NULL_POINTER; + + result = (*aRange)->SetStart(aStartParent, aStartOffset); + + if (NS_SUCCEEDED(result)) + result = (*aRange)->SetEnd(aEndParent, aEndOffset); + + if (NS_FAILED(result)) + { + NS_RELEASE((*aRange)); + *aRange = 0; + } + return result; +} + +nsresult +nsEditor::GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode) +{ + if (!aRange || !aNode) return NS_ERROR_NULL_POINTER; + + *aNode = nsnull; + + nsCOMPtr startParent; + nsresult res = aRange->GetStartParent(getter_AddRefs(startParent)); + if (NS_FAILED(res)) return res; + if (!startParent) return NS_ERROR_FAILURE; + + nsCOMPtr startNode; + PRInt32 offset; + res = aRange->GetStartOffset(&offset); + if (NS_FAILED(res)) return res; + + nsCOMPtr nodeList; + res = startParent->GetChildNodes(getter_AddRefs(nodeList)); + if (NS_FAILED(res)) return res; + if (!nodeList) return NS_ERROR_FAILURE; + + nsCOMPtr child; + res = nodeList->Item(offset,getter_AddRefs(child)); + if (NS_FAILED(res)) return res; + if (!child) return NS_ERROR_FAILURE; + + *aNode = child.get(); + NS_ADDREF(*aNode); + + return res; +} + +nsresult +nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode *aNode) +{ + if (!aNode) return NS_ERROR_NULL_POINTER; + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if(!selection) return NS_ERROR_FAILURE; + + nsCOMPtr parentNode; + res = aNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_FAILED(res)) return res; + if (!parentNode) return NS_ERROR_NULL_POINTER; + + PRInt32 offset; + res = GetChildOffset(aNode, parentNode, offset); + if (NS_FAILED(res)) return res; + + nsCOMPtr range; + res = CreateRange(parentNode, offset, parentNode, offset+1, getter_AddRefs(range)); + if (NS_FAILED(res)) return res; + if (!range) return NS_ERROR_NULL_POINTER; + + return selection->AddRange(range); +} + +nsresult nsEditor::ClearSelection() +{ + nsCOMPtr selection; + nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + return selection->ClearSelection(); +} + diff --git a/editor/base/nsEditor.h b/editor/base/nsEditor.h index 3ea590b2ad8c..ffa4ee9afa8d 100644 --- a/editor/base/nsEditor.h +++ b/editor/base/nsEditor.h @@ -650,6 +650,19 @@ public: static nsresult GetStartNodeAndOffset(nsIDOMSelection *aSelection, nsCOMPtr *outStartNode, PRInt32 *outStartOffset); static nsresult GetEndNodeAndOffset(nsIDOMSelection *aSelection, nsCOMPtr *outEndNode, PRInt32 *outEndOffset); + // Helpers to add a node to the selection. + // Used by table cell selection methods + nsresult CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset, + nsIDOMNode *aEndParent, PRInt32 aEndOffset, + nsIDOMRange **aRange); + // Gets the node at the StartOffset of StartParent in aRange + // (this is a table cell in cell selection mode) + nsresult GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode); + // Creates a range with just the supplied node and appends that to the selection + nsresult AppendNodeToSelectionAsRange(nsIDOMNode *aNode); + // When you are using AppendNodeToSelectionAsRange, call this first to start a new selection + nsresult ClearSelection(); + nsresult IsPreformatted(nsIDOMNode *aNode, PRBool *aResult); nsresult IsNextCharWhitespace(nsIDOMNode *aParentNode, PRInt32 aOffset, diff --git a/editor/base/nsEditorShell.cpp b/editor/base/nsEditorShell.cpp index b2dfdc194547..6071f0f668bc 100644 --- a/editor/base/nsEditorShell.cpp +++ b/editor/base/nsEditorShell.cpp @@ -1248,15 +1248,7 @@ nsEditorShell::CheckAndSaveDocument(const PRUnichar *reasonToSave, PRBool *_retv nsAutoString tmp2 = GetString("DontSave"); nsAutoString title; GetDocumentTitleString(title); - nsAutoString saveMsg = GetString("SaveFilePrompt")+" "+"\""+title+"\""; - if (ReasonToSave.Length() > 0) - { - saveMsg += " "; - saveMsg += ReasonToSave; - saveMsg += GetString("QuestionMark"); - } else { - saveMsg += GetString("QuestionMark"); - } + nsAutoString saveMsg = ((GetString("SaveFilePrompt")).ReplaceSubstring("%title%",title)).ReplaceSubstring("%reason%",ReasonToSave); EConfirmResult result = ConfirmWithCancel(GetString("SaveDocument"), saveMsg, &tmp1, &tmp2); @@ -3104,6 +3096,101 @@ nsEditorShell::JoinTableCells() return result; } +NS_IMETHODIMP +nsEditorShell::SelectTableCell() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectTableCell(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + +NS_IMETHODIMP +nsEditorShell::SelectTableRow() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectTableRow(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + +NS_IMETHODIMP +nsEditorShell::SelectTableColumn() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectTableColumn(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + +NS_IMETHODIMP +nsEditorShell::SelectTable() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectTable(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + +NS_IMETHODIMP +nsEditorShell::SelectAllTableCells() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectAllTableCells(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + NS_IMETHODIMP nsEditorShell::NormalizeTable(nsIDOMElement *aTable) { diff --git a/editor/base/nsHTMLEditor.cpp b/editor/base/nsHTMLEditor.cpp index e326edf1110c..3cd55d05e759 100644 --- a/editor/base/nsHTMLEditor.cpp +++ b/editor/base/nsHTMLEditor.cpp @@ -2636,7 +2636,9 @@ NODE_FOUND: NS_ADDREF(*aReturn); } } - return NS_OK; + else res = NS_EDITOR_ELEMENT_NOT_FOUND; + + return res; } NS_IMETHODIMP @@ -2835,7 +2837,9 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu // Getters must addref NS_ADDREF(*aReturn); } - } + } + else res = NS_EDITOR_ELEMENT_NOT_FOUND; + return res; } @@ -4990,7 +4994,7 @@ void nsHTMLEditor::ResetTextSelectionForRange(nsIDOMNode *aParent, //================================================================ // HTML Editor methods // -// Note: Table Editing methods are implemented in EditTable.cpp +// Note: Table Editing methods are implemented in nsTableEditor.cpp // NS_IMETHODIMP @@ -7375,6 +7379,19 @@ nsHTMLEditor::GetNextElementByTagName(nsIDOMElement *aCurrentElement, return res; } +NS_IMETHODIMP +nsHTMLEditor::SetSelectionAtDocumentStart(nsIDOMSelection *aSelection) +{ + nsCOMPtr bodyElement; + nsresult res = GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res)) + { + if (!bodyElement) return NS_ERROR_NULL_POINTER; + res = aSelection->Collapse(bodyElement,0); + } + return res; +} + #ifdef XP_MAC #pragma mark - #endif @@ -7739,4 +7756,3 @@ nsHTMLEditor::InsertContainerAbove(nsIDOMNode *inNode, return NS_OK; } - diff --git a/editor/base/nsHTMLEditor.h b/editor/base/nsHTMLEditor.h index 86cc5d06239d..cf78f86ff7f2 100644 --- a/editor/base/nsHTMLEditor.h +++ b/editor/base/nsHTMLEditor.h @@ -182,6 +182,11 @@ public: NS_IMETHOD DeleteTableCellContents(); NS_IMETHOD DeleteTableColumn(PRInt32 aNumber); NS_IMETHOD DeleteTableRow(PRInt32 aNumber); + NS_IMETHOD SelectTableCell(); + NS_IMETHOD SelectTableRow(); + NS_IMETHOD SelectTableColumn(); + NS_IMETHOD SelectTable(); + NS_IMETHOD SelectAllTableCells(); NS_IMETHOD JoinTableCells(); NS_IMETHOD NormalizeTable(nsIDOMElement *aTable); NS_IMETHOD GetCellIndexes(nsIDOMElement *aCell, PRInt32& aRowIndex, PRInt32& aColIndex); @@ -321,7 +326,7 @@ protected: NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode); NS_IMETHOD InsertBR(nsCOMPtr *outBRNode); -// Table Editing (implemented in EditTable.cpp) +// Table Editing (implemented in nsTableEditor.cpp) // Table utilities @@ -346,8 +351,15 @@ protected: nsCOMPtr &aCellParent, PRInt32& aCellOffset, PRInt32& aRow, PRInt32& aCol); - // Use the selection iterator to find the first cell in the selection - NS_IMETHOD GetFirstSelectedCell(nsCOMPtr &aCell); + // Finds the first selected cell in first range of selection + // This is in the *order of selection*, not order in the table + // (i.e., each cell added to selection is added in another range + // in the selection's rangelist, independent of location in table) + NS_IMETHOD GetFirstSelectedCell(nsIDOMElement **aCell); + + // Fallback method: Call this after using ClearSelection() and you + // failed to set selection to some other content in the document + NS_IMETHOD SetSelectionAtDocumentStart(nsIDOMSelection *aSelection); NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag, diff --git a/editor/base/nsHTMLEditorLog.cpp b/editor/base/nsHTMLEditorLog.cpp index 5f7342adcfb9..a9a138b6bfa2 100644 --- a/editor/base/nsHTMLEditorLog.cpp +++ b/editor/base/nsHTMLEditorLog.cpp @@ -504,6 +504,160 @@ nsHTMLEditorLog::SetBodyAttribute(const nsString& aAttr, const nsString& aValue) return nsHTMLEditor::SetBodyAttribute(aAttr, aValue); } +NS_IMETHODIMP +nsHTMLEditorLog:: InsertTableCell(PRInt32 aNumber, PRBool aAfter) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.InsertTableCell(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::InsertTableCell(aNumber, aAfter); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: InsertTableColumn(PRInt32 aNumber, PRBool aAfter) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.InsertTableColumn(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::InsertTableColumn(aNumber, aAfter); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: InsertTableRow(PRInt32 aNumber, PRBool aAfter) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.InsertTableRow(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::InsertTableRow(aNumber, aAfter); +} + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTable() +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTable();\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTable(); +} + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTableCell(PRInt32 aNumber) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTableCell(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTableCell(aNumber); +} + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTableCellContents() +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTableCellContents();\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTableCellContents(); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTableColumn(PRInt32 aNumber) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTableColumn(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTableColumn(aNumber); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTableRow(PRInt32 aNumber) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTableRow(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTableRow(aNumber); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: JoinTableCells() +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.JoinTableCells();\n"); + Flush(); + } + + return nsHTMLEditor::JoinTableCells(); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: NormalizeTable(nsIDOMElement *aTable) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.NormalizeTable(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::NormalizeTable(aTable); +} + + NS_IMETHODIMP nsHTMLEditorLog::MakeOrChangeList(const nsString& aListType) { diff --git a/editor/base/nsHTMLEditorLog.h b/editor/base/nsHTMLEditorLog.h index 260bce6211c0..7bef47f7ad7f 100644 --- a/editor/base/nsHTMLEditorLog.h +++ b/editor/base/nsHTMLEditorLog.h @@ -93,6 +93,18 @@ public: NS_IMETHOD Align(const nsString& aAlign); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); NS_IMETHOD InsertLinkAroundSelection(nsIDOMElement* aAnchorElement); + + /* Table Editing */ + NS_IMETHOD InsertTableCell(PRInt32 aNumber, PRBool aAfter); + NS_IMETHOD InsertTableColumn(PRInt32 aNumber, PRBool aAfter); + NS_IMETHOD InsertTableRow(PRInt32 aNumber, PRBool aAfter); + NS_IMETHOD DeleteTable(); + NS_IMETHOD DeleteTableCell(PRInt32 aNumber); + NS_IMETHOD DeleteTableCellContents(); + NS_IMETHOD DeleteTableColumn(PRInt32 aNumber); + NS_IMETHOD DeleteTableRow(PRInt32 aNumber); + NS_IMETHOD JoinTableCells(); + NS_IMETHOD NormalizeTable(nsIDOMElement *aTable); NS_IMETHOD StartLogging(nsIFileSpec *aLogFile); NS_IMETHOD StopLogging(); diff --git a/editor/base/EditTable.cpp b/editor/base/nsTableEditor.cpp similarity index 93% rename from editor/base/EditTable.cpp rename to editor/base/nsTableEditor.cpp index 89bb7f6ebf75..923dec7cc22a 100644 --- a/editor/base/EditTable.cpp +++ b/editor/base/nsTableEditor.cpp @@ -171,6 +171,8 @@ nsHTMLEditor::InsertTableCell(PRInt32 aNumber, PRBool aAfter) PRInt32 cellOffset, startRowIndex, startColIndex; nsresult res = GetCellContext(selection, table, curCell, cellParent, cellOffset, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!curCell) return NS_EDITOR_ELEMENT_NOT_FOUND; // Get more data for current cell in row we are inserting at (we need COLSPAN) PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; @@ -337,6 +339,8 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) PRInt32 cellOffset, startRowIndex, startColIndex; nsresult res = GetCellContext(selection, table, curCell, cellParent, cellOffset, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!curCell) return NS_EDITOR_ELEMENT_NOT_FOUND; // Get more data for current cell (we need ROWSPAN) PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; @@ -448,6 +452,8 @@ nsHTMLEditor::InsertTableRow(PRInt32 aNumber, PRBool aAfter) PRInt32 cellOffset, startRowIndex, startColIndex; nsresult res = GetCellContext(selection, table, curCell, cellParent, cellOffset, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!curCell) return NS_EDITOR_ELEMENT_NOT_FOUND; // Get more data for current cell in row we are inserting at (we need COLSPAN) PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; @@ -642,7 +648,8 @@ nsHTMLEditor::DeleteTableCell(PRInt32 aNumber) { res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; - if (!cell) return NS_ERROR_NULL_POINTER; + // Don't fail if no cell found + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; if (1 == GetNumberOfCellsInRow(table, startRowIndex)) { @@ -695,8 +702,8 @@ nsHTMLEditor::DeleteTableCellContents() res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; - if (!cell) return NS_ERROR_NULL_POINTER; - + // Don't fail if no cell found + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; // We clear the selection to avoid problems when nodes in the selection are deleted, selection->ClearSelection(); @@ -740,7 +747,8 @@ nsHTMLEditor::DeleteTableColumn(PRInt32 aNumber) res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; - if(!cell) return NS_ERROR_NULL_POINTER; + // Don't fail if no cell found + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; res = GetTableSize(table, rowCount, colCount); if (NS_FAILED(res)) return res; @@ -856,7 +864,8 @@ nsHTMLEditor::DeleteTableRow(PRInt32 aNumber) res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; - if(!cell) return NS_ERROR_NULL_POINTER; + // Don't fail if no cell found + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; res = GetTableSize(table, rowCount, colCount); if (NS_FAILED(res)) return res; @@ -928,6 +937,69 @@ nsHTMLEditor::DeleteTableRow(PRInt32 aNumber) return res; } + + +NS_IMETHODIMP +nsHTMLEditor::SelectTable() +{ + nsCOMPtr table; + nsresult res = NS_ERROR_FAILURE; + res = GetElementOrParentByTagName("table", nsnull, getter_AddRefs(table)); + if (NS_FAILED(res)) return res; + // Don't fail if we didn't find a table + if (!table) return NS_OK; + + nsCOMPtr tableNode = do_QueryInterface(table); + if (tableNode) + { + res = ClearSelection(); + if (NS_SUCCEEDED(res)) + res = AppendNodeToSelectionAsRange(table); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::SelectTableCell() +{ + nsCOMPtr cell; + nsresult res = NS_ERROR_FAILURE; + res = GetElementOrParentByTagName("td", nsnull, getter_AddRefs(cell)); + if (NS_FAILED(res)) return res; + // Don't fail if we didn't find a table + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + nsCOMPtr cellNode = do_QueryInterface(cell); + if (cellNode) + { + res = ClearSelection(); + if (NS_SUCCEEDED(res)) + res = AppendNodeToSelectionAsRange(cellNode); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::SelectAllTableCells() +{ + nsresult res = NS_OK; + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::SelectTableRow() +{ + nsresult res = NS_OK; + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::SelectTableColumn() +{ + nsresult res = NS_OK; + return res; +} + NS_IMETHODIMP nsHTMLEditor::JoinTableCells() { @@ -1275,11 +1347,14 @@ nsHTMLEditor::GetCellDataAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aC // Note that this returns NS_TABLELAYOUT_CELL_NOT_FOUND when // the index(es) are out of bounds - return tableLayoutObject->GetCellDataAt(aRowIndex, aColIndex, aCell, - aStartRowIndex, aStartColIndex, - aRowSpan, aColSpan, - aActualRowSpan, aActualColSpan, - aIsSelected); + res = tableLayoutObject->GetCellDataAt(aRowIndex, aColIndex, aCell, + aStartRowIndex, aStartColIndex, + aRowSpan, aColSpan, + aActualRowSpan, aActualColSpan, + aIsSelected); + // Convert to editor's generic "not found" return value + if (res == NS_TABLELAYOUT_CELL_NOT_FOUND) res = NS_EDITOR_ELEMENT_NOT_FOUND; + return res; } // When all you want is the cell @@ -1304,7 +1379,7 @@ nsHTMLEditor::GetCellContext(nsCOMPtr &aSelection, if (!aSelection) return NS_ERROR_FAILURE; // Find the first selected cell - res = GetFirstSelectedCell(aCell); + res = GetFirstSelectedCell(getter_AddRefs(aCell)); if (!aCell) { //If a cell wasn't selected, then assume the selection is INSIDE @@ -1338,15 +1413,18 @@ nsHTMLEditor::GetCellContext(nsCOMPtr &aSelection, } NS_IMETHODIMP -nsHTMLEditor::GetFirstSelectedCell(nsCOMPtr &aCell) +nsHTMLEditor::GetFirstSelectedCell(nsIDOMElement **aCell) { + if (!aCell) return NS_ERROR_NULL_POINTER; + *aCell = nsnull; + nsCOMPtr selection; nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_FAILURE; - // The most straight forward way of finding a selected cell - // is to use the selection iterator +//TODO: Replace this with code below new "table cell mode" flag is implemented + nsCOMPtr enumerator; res = selection->GetEnumerator(getter_AddRefs(enumerator)); @@ -1381,13 +1459,42 @@ nsHTMLEditor::GetFirstSelectedCell(nsCOMPtr &aCell) atom.get() == nsIEditProperty::th ) { // We found a cell - aCell = do_QueryInterface(content); - break; + nsCOMPtr cellElement = do_QueryInterface(content); + if (cellElement) + { + *aCell = cellElement.get(); + NS_ADDREF(*aCell); + } + return NS_OK; } iter->Next(); } } - return res; + return NS_EDITOR_ELEMENT_NOT_FOUND; + +#if 0 +//TODO: Do this only after checking the new "table cell mode" flag + // The first cell is the starting node in the first selection range + nsCOMPtr firstRange; + res = selection->GetRangeAt(0, getter_AddRefs(firstRange)); + if (NS_FAILED(res)) return res; + if (!firstRange) return NS_ERROR_FAILURE; + + nsCOMPtr cellNode; + res = GetFirstNodeInRange(firstRange, getter_AddRefs(cellNode)); + if (NS_FAILED(res)) return res; + if (!cellNode) return NS_ERROR_FAILURE; + nsCOMPtr cellElement = do_QueryInterface(cellNode); + + if (cellElement) + { + *aCell = cellElement.get(); + NS_ADDREF(*aCell); + } + else res = NS_EDITOR_ELEMENT_NOT_FOUND; + + return res; +#endif } NS_IMETHODIMP @@ -1468,17 +1575,10 @@ nsHTMLEditor::SetCaretAfterTableEdit(nsIDOMElement* aTable, PRInt32 aRow, PRInt3 { if(NS_SUCCEEDED(GetChildOffset(aTable, tableParent, tableOffset))) return selection->Collapse(tableParent, tableOffset); - - // Still failing! Set selection to start of doc - nsCOMPtr bodyElement; - res = GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_SUCCEEDED(res)) - { - if (!bodyElement) return NS_ERROR_NULL_POINTER; - res = selection->Collapse(bodyElement,0); - } } - return res; + // Last resort: Set selection to start of doc + // (it's very bad to not have a valid selection!) + return SetSelectionAtDocumentStart(selection); } NS_IMETHODIMP @@ -1504,7 +1604,7 @@ nsHTMLEditor::GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsS nsCOMPtr firstCell; nsCOMPtr tableElement; - res = GetFirstSelectedCell(tableElement); + res = GetFirstSelectedCell(getter_AddRefs(tableElement)); if(NS_FAILED(res)) return res; if (tableElement) { diff --git a/editor/composer/src/nsEditorShell.cpp b/editor/composer/src/nsEditorShell.cpp index b2dfdc194547..6071f0f668bc 100644 --- a/editor/composer/src/nsEditorShell.cpp +++ b/editor/composer/src/nsEditorShell.cpp @@ -1248,15 +1248,7 @@ nsEditorShell::CheckAndSaveDocument(const PRUnichar *reasonToSave, PRBool *_retv nsAutoString tmp2 = GetString("DontSave"); nsAutoString title; GetDocumentTitleString(title); - nsAutoString saveMsg = GetString("SaveFilePrompt")+" "+"\""+title+"\""; - if (ReasonToSave.Length() > 0) - { - saveMsg += " "; - saveMsg += ReasonToSave; - saveMsg += GetString("QuestionMark"); - } else { - saveMsg += GetString("QuestionMark"); - } + nsAutoString saveMsg = ((GetString("SaveFilePrompt")).ReplaceSubstring("%title%",title)).ReplaceSubstring("%reason%",ReasonToSave); EConfirmResult result = ConfirmWithCancel(GetString("SaveDocument"), saveMsg, &tmp1, &tmp2); @@ -3104,6 +3096,101 @@ nsEditorShell::JoinTableCells() return result; } +NS_IMETHODIMP +nsEditorShell::SelectTableCell() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectTableCell(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + +NS_IMETHODIMP +nsEditorShell::SelectTableRow() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectTableRow(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + +NS_IMETHODIMP +nsEditorShell::SelectTableColumn() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectTableColumn(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + +NS_IMETHODIMP +nsEditorShell::SelectTable() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectTable(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + +NS_IMETHODIMP +nsEditorShell::SelectAllTableCells() +{ + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->SelectAllTableCells(); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + NS_IMETHODIMP nsEditorShell::NormalizeTable(nsIDOMElement *aTable) { diff --git a/editor/idl/nsIEditorShell.idl b/editor/idl/nsIEditorShell.idl index b6c7030fc452..3eb95d47fe3c 100644 --- a/editor/idl/nsIEditorShell.idl +++ b/editor/idl/nsIEditorShell.idl @@ -298,6 +298,15 @@ interface nsIEditorShell : nsISupports void DeleteTableRow(in PRInt32 number); void DeleteTableColumn(in PRInt32 number); void JoinTableCells(); + /** Table selection methods + * Selecting a row or column actually + * selects all cells (not TR in the case of rows) + */ + void SelectTableCell(); + void SelectTableRow(); + void SelectTableColumn(); + void SelectTable(); + void SelectAllTableCells(); /** Scan through all rows and add cells as needed so * all locations in the cellmap are occupied. diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 1d18032b55e6..6dd07be6cbcc 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -106,6 +106,7 @@ static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_CID(kCDOMRangeCID, NS_RANGE_CID); // transaction manager static NS_DEFINE_CID(kCTransactionManagerCID, NS_TRANSACTIONMANAGER_CID); @@ -4949,3 +4950,100 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, return result; } +nsresult +nsEditor::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset, + nsIDOMNode *aEndParent, PRInt32 aEndOffset, + nsIDOMRange **aRange) +{ + nsresult result; + result = nsComponentManager::CreateInstance(kCDOMRangeCID, nsnull, + NS_GET_IID(nsIDOMRange), + (void **)aRange); + + if (NS_FAILED(result)) + return result; + + if (!*aRange) + return NS_ERROR_NULL_POINTER; + + result = (*aRange)->SetStart(aStartParent, aStartOffset); + + if (NS_SUCCEEDED(result)) + result = (*aRange)->SetEnd(aEndParent, aEndOffset); + + if (NS_FAILED(result)) + { + NS_RELEASE((*aRange)); + *aRange = 0; + } + return result; +} + +nsresult +nsEditor::GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode) +{ + if (!aRange || !aNode) return NS_ERROR_NULL_POINTER; + + *aNode = nsnull; + + nsCOMPtr startParent; + nsresult res = aRange->GetStartParent(getter_AddRefs(startParent)); + if (NS_FAILED(res)) return res; + if (!startParent) return NS_ERROR_FAILURE; + + nsCOMPtr startNode; + PRInt32 offset; + res = aRange->GetStartOffset(&offset); + if (NS_FAILED(res)) return res; + + nsCOMPtr nodeList; + res = startParent->GetChildNodes(getter_AddRefs(nodeList)); + if (NS_FAILED(res)) return res; + if (!nodeList) return NS_ERROR_FAILURE; + + nsCOMPtr child; + res = nodeList->Item(offset,getter_AddRefs(child)); + if (NS_FAILED(res)) return res; + if (!child) return NS_ERROR_FAILURE; + + *aNode = child.get(); + NS_ADDREF(*aNode); + + return res; +} + +nsresult +nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode *aNode) +{ + if (!aNode) return NS_ERROR_NULL_POINTER; + nsCOMPtr selection; + nsresult res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if(!selection) return NS_ERROR_FAILURE; + + nsCOMPtr parentNode; + res = aNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_FAILED(res)) return res; + if (!parentNode) return NS_ERROR_NULL_POINTER; + + PRInt32 offset; + res = GetChildOffset(aNode, parentNode, offset); + if (NS_FAILED(res)) return res; + + nsCOMPtr range; + res = CreateRange(parentNode, offset, parentNode, offset+1, getter_AddRefs(range)); + if (NS_FAILED(res)) return res; + if (!range) return NS_ERROR_NULL_POINTER; + + return selection->AddRange(range); +} + +nsresult nsEditor::ClearSelection() +{ + nsCOMPtr selection; + nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + return selection->ClearSelection(); +} + diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index 3ea590b2ad8c..ffa4ee9afa8d 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -650,6 +650,19 @@ public: static nsresult GetStartNodeAndOffset(nsIDOMSelection *aSelection, nsCOMPtr *outStartNode, PRInt32 *outStartOffset); static nsresult GetEndNodeAndOffset(nsIDOMSelection *aSelection, nsCOMPtr *outEndNode, PRInt32 *outEndOffset); + // Helpers to add a node to the selection. + // Used by table cell selection methods + nsresult CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset, + nsIDOMNode *aEndParent, PRInt32 aEndOffset, + nsIDOMRange **aRange); + // Gets the node at the StartOffset of StartParent in aRange + // (this is a table cell in cell selection mode) + nsresult GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode); + // Creates a range with just the supplied node and appends that to the selection + nsresult AppendNodeToSelectionAsRange(nsIDOMNode *aNode); + // When you are using AppendNodeToSelectionAsRange, call this first to start a new selection + nsresult ClearSelection(); + nsresult IsPreformatted(nsIDOMNode *aNode, PRBool *aResult); nsresult IsNextCharWhitespace(nsIDOMNode *aParentNode, PRInt32 aOffset, diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp index e326edf1110c..3cd55d05e759 100644 --- a/editor/libeditor/html/nsHTMLEditor.cpp +++ b/editor/libeditor/html/nsHTMLEditor.cpp @@ -2636,7 +2636,9 @@ NODE_FOUND: NS_ADDREF(*aReturn); } } - return NS_OK; + else res = NS_EDITOR_ELEMENT_NOT_FOUND; + + return res; } NS_IMETHODIMP @@ -2835,7 +2837,9 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu // Getters must addref NS_ADDREF(*aReturn); } - } + } + else res = NS_EDITOR_ELEMENT_NOT_FOUND; + return res; } @@ -4990,7 +4994,7 @@ void nsHTMLEditor::ResetTextSelectionForRange(nsIDOMNode *aParent, //================================================================ // HTML Editor methods // -// Note: Table Editing methods are implemented in EditTable.cpp +// Note: Table Editing methods are implemented in nsTableEditor.cpp // NS_IMETHODIMP @@ -7375,6 +7379,19 @@ nsHTMLEditor::GetNextElementByTagName(nsIDOMElement *aCurrentElement, return res; } +NS_IMETHODIMP +nsHTMLEditor::SetSelectionAtDocumentStart(nsIDOMSelection *aSelection) +{ + nsCOMPtr bodyElement; + nsresult res = GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res)) + { + if (!bodyElement) return NS_ERROR_NULL_POINTER; + res = aSelection->Collapse(bodyElement,0); + } + return res; +} + #ifdef XP_MAC #pragma mark - #endif @@ -7739,4 +7756,3 @@ nsHTMLEditor::InsertContainerAbove(nsIDOMNode *inNode, return NS_OK; } - diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h index 86cc5d06239d..cf78f86ff7f2 100644 --- a/editor/libeditor/html/nsHTMLEditor.h +++ b/editor/libeditor/html/nsHTMLEditor.h @@ -182,6 +182,11 @@ public: NS_IMETHOD DeleteTableCellContents(); NS_IMETHOD DeleteTableColumn(PRInt32 aNumber); NS_IMETHOD DeleteTableRow(PRInt32 aNumber); + NS_IMETHOD SelectTableCell(); + NS_IMETHOD SelectTableRow(); + NS_IMETHOD SelectTableColumn(); + NS_IMETHOD SelectTable(); + NS_IMETHOD SelectAllTableCells(); NS_IMETHOD JoinTableCells(); NS_IMETHOD NormalizeTable(nsIDOMElement *aTable); NS_IMETHOD GetCellIndexes(nsIDOMElement *aCell, PRInt32& aRowIndex, PRInt32& aColIndex); @@ -321,7 +326,7 @@ protected: NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode); NS_IMETHOD InsertBR(nsCOMPtr *outBRNode); -// Table Editing (implemented in EditTable.cpp) +// Table Editing (implemented in nsTableEditor.cpp) // Table utilities @@ -346,8 +351,15 @@ protected: nsCOMPtr &aCellParent, PRInt32& aCellOffset, PRInt32& aRow, PRInt32& aCol); - // Use the selection iterator to find the first cell in the selection - NS_IMETHOD GetFirstSelectedCell(nsCOMPtr &aCell); + // Finds the first selected cell in first range of selection + // This is in the *order of selection*, not order in the table + // (i.e., each cell added to selection is added in another range + // in the selection's rangelist, independent of location in table) + NS_IMETHOD GetFirstSelectedCell(nsIDOMElement **aCell); + + // Fallback method: Call this after using ClearSelection() and you + // failed to set selection to some other content in the document + NS_IMETHOD SetSelectionAtDocumentStart(nsIDOMSelection *aSelection); NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag, diff --git a/editor/libeditor/html/nsHTMLEditorLog.cpp b/editor/libeditor/html/nsHTMLEditorLog.cpp index 5f7342adcfb9..a9a138b6bfa2 100644 --- a/editor/libeditor/html/nsHTMLEditorLog.cpp +++ b/editor/libeditor/html/nsHTMLEditorLog.cpp @@ -504,6 +504,160 @@ nsHTMLEditorLog::SetBodyAttribute(const nsString& aAttr, const nsString& aValue) return nsHTMLEditor::SetBodyAttribute(aAttr, aValue); } +NS_IMETHODIMP +nsHTMLEditorLog:: InsertTableCell(PRInt32 aNumber, PRBool aAfter) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.InsertTableCell(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::InsertTableCell(aNumber, aAfter); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: InsertTableColumn(PRInt32 aNumber, PRBool aAfter) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.InsertTableColumn(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::InsertTableColumn(aNumber, aAfter); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: InsertTableRow(PRInt32 aNumber, PRBool aAfter) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.InsertTableRow(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::InsertTableRow(aNumber, aAfter); +} + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTable() +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTable();\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTable(); +} + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTableCell(PRInt32 aNumber) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTableCell(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTableCell(aNumber); +} + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTableCellContents() +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTableCellContents();\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTableCellContents(); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTableColumn(PRInt32 aNumber) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTableColumn(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTableColumn(aNumber); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: DeleteTableRow(PRInt32 aNumber) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.DeleteTableRow(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::DeleteTableRow(aNumber); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: JoinTableCells() +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.JoinTableCells();\n"); + Flush(); + } + + return nsHTMLEditor::JoinTableCells(); +} + + +NS_IMETHODIMP +nsHTMLEditorLog:: NormalizeTable(nsIDOMElement *aTable) +{ + nsAutoHTMLEditorLogLock logLock(this); + + if (!mLocked && mFileSpec) + { + Write("window.editorShell.NormalizeTable(\""); + Write("\");\n"); + Flush(); + } + + return nsHTMLEditor::NormalizeTable(aTable); +} + + NS_IMETHODIMP nsHTMLEditorLog::MakeOrChangeList(const nsString& aListType) { diff --git a/editor/libeditor/html/nsHTMLEditorLog.h b/editor/libeditor/html/nsHTMLEditorLog.h index 260bce6211c0..7bef47f7ad7f 100644 --- a/editor/libeditor/html/nsHTMLEditorLog.h +++ b/editor/libeditor/html/nsHTMLEditorLog.h @@ -93,6 +93,18 @@ public: NS_IMETHOD Align(const nsString& aAlign); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); NS_IMETHOD InsertLinkAroundSelection(nsIDOMElement* aAnchorElement); + + /* Table Editing */ + NS_IMETHOD InsertTableCell(PRInt32 aNumber, PRBool aAfter); + NS_IMETHOD InsertTableColumn(PRInt32 aNumber, PRBool aAfter); + NS_IMETHOD InsertTableRow(PRInt32 aNumber, PRBool aAfter); + NS_IMETHOD DeleteTable(); + NS_IMETHOD DeleteTableCell(PRInt32 aNumber); + NS_IMETHOD DeleteTableCellContents(); + NS_IMETHOD DeleteTableColumn(PRInt32 aNumber); + NS_IMETHOD DeleteTableRow(PRInt32 aNumber); + NS_IMETHOD JoinTableCells(); + NS_IMETHOD NormalizeTable(nsIDOMElement *aTable); NS_IMETHOD StartLogging(nsIFileSpec *aLogFile); NS_IMETHOD StopLogging(); diff --git a/editor/libeditor/html/nsTableEditor.cpp b/editor/libeditor/html/nsTableEditor.cpp new file mode 100644 index 000000000000..923dec7cc22a --- /dev/null +++ b/editor/libeditor/html/nsTableEditor.cpp @@ -0,0 +1,1649 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Pierre Phaneuf + */ + + +#include "nsIDOMDocument.h" +#include "nsEditor.h" +#include "nsIDOMText.h" +#include "nsIDOMElement.h" +#include "nsIDOMAttr.h" +#include "nsIDOMNode.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMRange.h" +#include "nsIDOMSelection.h" +#include "nsLayoutCID.h" +#include "nsIContent.h" +#include "nsIContentIterator.h" +#include "nsIAtom.h" +#include "nsIDOMHTMLTableElement.h" +#include "nsIDOMHTMLTableCellElement.h" +#include "nsITableCellLayout.h" // For efficient access to table cell +#include "nsITableLayout.h" // data owned by the table and cell frames +#include "nsHTMLEditor.h" + +#include "nsEditorUtils.h" + +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); + +/*************************************************************************** + * stack based helper class for restoring selection after table edit + */ +class nsSetCaretAfterTableEdit +{ + private: + nsCOMPtr mEd; + nsCOMPtr mTable; + PRInt32 mCol, mRow, mDirection; + public: + nsSetCaretAfterTableEdit(nsITableEditor *aEd, nsIDOMElement* aTable, + PRInt32 aRow, PRInt32 aCol, PRInt32 aDirection) : + mEd(do_QueryInterface(aEd)) + { + mTable = aTable; + mRow = aRow; + mCol = aCol; + mDirection = aDirection; + } + + ~nsSetCaretAfterTableEdit() + { + if (mEd) + mEd->SetCaretAfterTableEdit(mTable, mRow, mCol, mDirection); + } + // This is needed to abort the caret reset in the destructor + // when one method yields control to another + void CancelSetCaret() {mEd = nsnull; mTable = nsnull;} +}; + +// Table Editing helper utilities (not exposed in IDL) + +NS_IMETHODIMP +nsHTMLEditor::InsertCell(nsIDOMElement *aCell, PRInt32 aRowSpan, PRInt32 aColSpan, + PRBool aAfter, nsIDOMElement **aNewCell) +{ + if (!aCell) return NS_ERROR_NULL_POINTER; + if (aNewCell) *aNewCell = nsnull; + + // And the parent and offsets needed to do an insert + nsCOMPtr cellParent; + nsresult res = aCell->GetParentNode(getter_AddRefs(cellParent)); + if( NS_SUCCEEDED(res) && cellParent) + { + PRInt32 cellOffset; + res = GetChildOffset(aCell, cellParent, cellOffset); + if( NS_SUCCEEDED(res)) + { + nsCOMPtr newCell; + res = CreateElementWithDefaults("td", getter_AddRefs(newCell)); + if(NS_FAILED(res)) return res; + if(!newCell) return NS_ERROR_FAILURE; + + //Optional: return new cell created + if (aNewCell) + { + *aNewCell = newCell.get(); + NS_ADDREF(*aNewCell); + } + if( aRowSpan > 1) + { + nsAutoString newRowSpan(aRowSpan); + // Note: Do NOT use editor txt for this + newCell->SetAttribute("rowspan", newRowSpan); + } + if( aColSpan > 1) + { + nsAutoString newColSpan(aColSpan); + // Note: Do NOT use editor txt for this + newCell->SetAttribute("colspan", newColSpan); + } + if(aAfter) cellOffset++; + + //Don't let Rules System change the selection + nsAutoTxnsConserveSelection dontChangeSelection(this); + res = nsEditor::InsertNode(newCell, cellParent, cellOffset); + } + } + return res; +} + +PRBool IsRowNode(nsCOMPtr &aNode) +{ + nsCOMPtr atom; + nsCOMPtr content = do_QueryInterface(aNode); + if (content) + { + content->GetTag(*getter_AddRefs(atom)); + if (atom && atom.get() == nsIEditProperty::tr) + return PR_TRUE; + } + return PR_FALSE; +} + +NS_IMETHODIMP nsHTMLEditor::SetColSpan(nsIDOMElement *aCell, PRInt32 aColSpan) +{ + if (!aCell) return NS_ERROR_NULL_POINTER; + nsAutoString newSpan; + newSpan.Append(aColSpan, 10); + nsAutoString colSpan("colspan"); + return SetAttribute(aCell, colSpan, newSpan); +} + +NS_IMETHODIMP nsHTMLEditor::SetRowSpan(nsIDOMElement *aCell, PRInt32 aRowSpan) +{ + if (!aCell) return NS_ERROR_NULL_POINTER; + nsAutoString newSpan; + newSpan.Append(aRowSpan, 10); + nsAutoString rowSpan("rowspan"); + return SetAttribute(aCell, rowSpan, newSpan); +} + +/****************************************************************/ + +// Table Editing interface methods + +NS_IMETHODIMP +nsHTMLEditor::InsertTableCell(PRInt32 aNumber, PRBool aAfter) +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr curCell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex; + nsresult res = GetCellContext(selection, table, curCell, cellParent, cellOffset, startRowIndex, startColIndex); + if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!curCell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + // Get more data for current cell in row we are inserting at (we need COLSPAN) + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + res = GetCellDataAt(table, startRowIndex, startColIndex, *getter_AddRefs(curCell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + if (NS_FAILED(res)) return res; + if (!curCell) return NS_ERROR_FAILURE; + PRInt32 newCellIndex = aAfter ? (startColIndex+colSpan) : startColIndex; + //We control selection resetting after the insert... + nsSetCaretAfterTableEdit setCaret(this, table, startRowIndex, newCellIndex, ePreviousColumn); + //...so suppress Rules System selection munging + nsAutoTxnsConserveSelection dontChangeSelection(this); + + PRInt32 i; + for (i = 0; i < aNumber; i++) + { + nsCOMPtr newCell; + res = CreateElementWithDefaults("td", getter_AddRefs(newCell)); + if (NS_SUCCEEDED(res) && newCell) + { + if (aAfter) cellOffset++; + res = nsEditor::InsertNode(newCell, cellParent, cellOffset); + if(NS_FAILED(res)) break; + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetFirstRow(nsIDOMElement* aTableElement, nsIDOMElement* &aRow) +{ + aRow = nsnull; + + nsCOMPtr tableElement; + nsresult res = GetElementOrParentByTagName("table", aTableElement, getter_AddRefs(tableElement)); + if (NS_FAILED(res)) return res; + if (!tableElement) return NS_ERROR_NULL_POINTER; + + nsCOMPtrtableNode = do_QueryInterface(tableElement); + if (!tableNode) return NS_ERROR_NULL_POINTER; + + nsCOMPtr tableChild; + res = tableNode->GetFirstChild(getter_AddRefs(tableChild)); + if (NS_FAILED(res)) return res; + + while (tableChild) + { + nsCOMPtr content = do_QueryInterface(tableChild); + if (content) + { + nsCOMPtr element; + nsCOMPtr atom; + content->GetTag(*getter_AddRefs(atom)); + if (atom.get() == nsIEditProperty::tr) + { + // Found a row directly under + element = do_QueryInterface(tableChild); + if(element) + { + aRow = element.get(); + NS_ADDREF(aRow); + } + return NS_OK; + } + // Look for row in one of the row container elements + if (atom.get() == nsIEditProperty::tbody || + atom.get() == nsIEditProperty::thead || + atom.get() == nsIEditProperty::tfoot ) + { + nsCOMPtr rowNode; + // All children should be rows + res = tableChild->GetFirstChild(getter_AddRefs(rowNode)); + if (NS_FAILED(res)) return res; + if (rowNode && IsRowNode(rowNode)) + { + element = do_QueryInterface(rowNode); + if(element) + { + aRow = element.get(); + NS_ADDREF(aRow); + } + return NS_OK; + } + } + } + // Here if table child was a CAPTION or COLGROUP + // or child of a row-conainer wasn't a row (bad HTML) + // Look in next table child + res = tableChild->GetNextSibling(getter_AddRefs(tableChild)); + }; + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetNextRow(nsIDOMElement* aTableElement, nsIDOMElement* &aRow) +{ + aRow = nsnull; + + nsCOMPtr rowElement; + nsresult res = GetElementOrParentByTagName("tr", aTableElement, getter_AddRefs(rowElement)); + if (NS_FAILED(res)) return res; + if (!rowElement) return NS_ERROR_NULL_POINTER; + + nsCOMPtr rowNode = do_QueryInterface(rowElement); + if (!rowNode) return NS_ERROR_NULL_POINTER; + + nsCOMPtr nextRow; + nsCOMPtr rowParent; + nsCOMPtr parentSibling; + nsCOMPtr element; + + rowNode->GetNextSibling(getter_AddRefs(nextRow)); + if(nextRow) + { + element = do_QueryInterface(nextRow); + if(element) + { + aRow = element.get(); + NS_ADDREF(aRow); + } + return NS_OK; + } + // No row found, search for rows in other table sections + res = rowNode->GetParentNode(getter_AddRefs(rowParent)); + if(NS_FAILED(res)) return res; + if (!rowParent) return NS_ERROR_NULL_POINTER; + + res = rowParent->GetNextSibling(getter_AddRefs(parentSibling)); + if(NS_FAILED(res)) return res; + + while (parentSibling) + { + res = parentSibling->GetFirstChild(getter_AddRefs(nextRow)); + if(NS_FAILED(res)) return res; + if (nextRow && IsRowNode(nextRow)) + { + element = do_QueryInterface(nextRow); + if(element) + { + aRow = element.get(); + NS_ADDREF(aRow); + } + return NS_OK; + } +#ifdef DEBUG + printf("GetNextRow: firstChild of row's parent's sibling is not a TR!\n"); +#endif + // We arrive here only if a table section has no children + // or first child of section is not a row (bad HTML!) + res = parentSibling->GetNextSibling(getter_AddRefs(parentSibling)); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr curCell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex; + nsresult res = GetCellContext(selection, table, curCell, cellParent, cellOffset, startRowIndex, startColIndex); + if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!curCell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + // Get more data for current cell (we need ROWSPAN) + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + res = GetCellDataAt(table, startRowIndex, startColIndex, *getter_AddRefs(curCell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + if (NS_FAILED(res)) return res; + if (!curCell) return NS_ERROR_FAILURE; + + nsAutoEditBatch beginBatching(this); + + // Use column after current cell if requested + if (aAfter) + { + startColIndex += colSpan; + //Detect when user is adding after a COLSPAN=0 case + // Assume they want to stop the "0" behavior and + // really add a new column. Thus we set the + // colspan to its true value + if (colSpan == 0) + SetColSpan(curCell, actualColSpan); + } + + PRInt32 rowCount, colCount, rowIndex; + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + PRInt32 lastColumn = colCount - 1; + + //We reset caret in destructor... + nsSetCaretAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow); + //.. so suppress Rules System selection munging + nsAutoTxnsConserveSelection dontChangeSelection(this); + + nsCOMPtr rowElement; + for ( rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + if (startColIndex < colCount) + { + // We are inserting before an existing column + res = GetCellDataAt(table, rowIndex, startColIndex, *getter_AddRefs(curCell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + if (NS_FAILED(res)) return res; + + // Don't fail entire process if we fail to find a cell + // (may fail just in particular rows with < adequate cells per row) + if (curCell) + { + if (curStartColIndex < startColIndex) + { + // We have a cell spanning this location + // Simply increase its colspan to keep table rectangular + // Note: we do nothing if colsSpan=0, + // since it should automatically span the new column + if (colSpan > 0) + SetColSpan(curCell, colSpan+aNumber); + } else { + // Simply set selection to the current cell + // so we can let InsertTableCell() do the work + // Insert a new cell before current one + selection->Collapse(curCell, 0); + res = InsertTableCell(aNumber, PR_FALSE); + } + } + } else { + // We are inserting after all existing columns + //TODO: Make sure table is "well formed" (call NormalizeTable) + // before appending new column + + // Get current row and append new cells after last cell in row + if(rowIndex == 0) + res = GetFirstRow(table.get(), *getter_AddRefs(rowElement)); + else + res = GetNextRow(rowElement.get(), *getter_AddRefs(rowElement)); + if (NS_FAILED(res)) return res; + + nsCOMPtr lastCell; + nsCOMPtr rowNode = do_QueryInterface(rowElement); + if (!rowElement) return NS_ERROR_FAILURE; + + res = rowElement->GetLastChild(getter_AddRefs(lastCell)); + if (NS_FAILED(res)) return res; + if (!lastCell) return NS_ERROR_FAILURE; + curCell = do_QueryInterface(lastCell); + if (curCell) + { + // Simply add same number of cells to each row + // Although tempted to check cell indexes for curCell, + // the effects of COLSPAN>1 in some cells makes this futile! + // We must use NormalizeTable first to assure proper + // that there are cells in each cellmap location + selection->Collapse(curCell, 0); + res = InsertTableCell(aNumber, PR_TRUE); + } + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::InsertTableRow(PRInt32 aNumber, PRBool aAfter) +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr curCell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex; + nsresult res = GetCellContext(selection, table, curCell, cellParent, cellOffset, startRowIndex, startColIndex); + if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!curCell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + // Get more data for current cell in row we are inserting at (we need COLSPAN) + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + res = GetCellDataAt(table, startRowIndex, startColIndex, *getter_AddRefs(curCell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + if (NS_FAILED(res)) return res; + if (!curCell) return NS_ERROR_FAILURE; + + nsCOMPtr parentRow; + res = GetElementOrParentByTagName("tr", curCell, getter_AddRefs(parentRow)); + if (NS_FAILED(res)) return res; + if (!parentRow) return NS_ERROR_NULL_POINTER; + + PRInt32 rowCount, colCount; + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + // Get the parent and offset where we will insert new row(s) + nsCOMPtr parentOfRow; + PRInt32 newRowOffset; + parentRow->GetParentNode(getter_AddRefs(parentOfRow)); + if (!parentOfRow) return NS_ERROR_NULL_POINTER; + res = GetChildOffset(parentRow, parentOfRow, newRowOffset); + if (NS_FAILED(res)) return res; + if (!parentOfRow) return NS_ERROR_NULL_POINTER; + + nsAutoEditBatch beginBatching(this); + + if (aAfter) + { + // Use row after current cell + startRowIndex += actualRowSpan; + // offset to use for new row insert + newRowOffset += actualRowSpan; + + //Detect when user is adding after a ROWSPAN=0 case + // Assume they want to stop the "0" behavior and + // really add a new row. Thus we set the + // rowspan to its true value + if (rowSpan == 0) + SetRowSpan(curCell, actualRowSpan); + } + + //We control selection resetting after the insert... + nsSetCaretAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn); + //...so suppress Rules System selection munging + nsAutoTxnsConserveSelection dontChangeSelection(this); + + PRInt32 cellsInRow = 0; + if (startRowIndex < rowCount) + { + // We are inserting above an existing row + // Get each cell in the insert row to adjust for COLSPAN effects while we + // count how many cells are needed + PRInt32 colIndex = 0; + // This returns NS_TABLELAYOUT_CELL_NOT_FOUND when we run past end of row, + // which passes the NS_SUCCEEDED macro + while ( NS_OK == GetCellDataAt(table, newRowOffset, colIndex, *getter_AddRefs(curCell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected) ) + { + if (curCell) + { + if (curStartRowIndex < startRowIndex) + { + // We have a cell spanning this location + // Simply increase its rowspan + //Note that if rowSpan == 0, we do nothing, + // since that cell should automatically extend into the new row + if (rowSpan > 0) + SetRowSpan(curCell, rowSpan+aNumber); + } else { + // Count the number of cells we need to add to the new row + cellsInRow += actualColSpan; + } + // Next cell in row + colIndex += actualColSpan; + } + else + colIndex++; + } + } else { + // We are adding a new row after all others + // If it weren't for colspan=0 effect, + // we could simply use colCount for number of new cells... + cellsInRow = colCount; + + // ...but we must compensate for all cells with rowSpan = 0 in the last row + PRInt32 lastRow = rowCount-1; + PRInt32 tempColIndex = 0; + while ( NS_OK == GetCellDataAt(table, lastRow, tempColIndex, *getter_AddRefs(curCell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected) ) + { + if (rowSpan == 0) + cellsInRow -= actualColSpan; + + tempColIndex += actualColSpan; + } + } + + if (cellsInRow > 0) + { + for (PRInt32 row = 0; row < aNumber; row++) + { + // Create a new row + nsCOMPtr newRow; + res = CreateElementWithDefaults("tr", getter_AddRefs(newRow)); + if (NS_SUCCEEDED(res)) + { + if (!newRow) return NS_ERROR_FAILURE; + + for (PRInt32 i = 0; i < cellsInRow; i++) + { + nsCOMPtr newCell; + res = CreateElementWithDefaults("td", getter_AddRefs(newCell)); + if (NS_FAILED(res)) return res; + if (!newCell) return NS_ERROR_FAILURE; + + // Don't use transaction system yet! (not until entire row is inserted) + nsCOMPtrresultNode; + res = newRow->AppendChild(newCell, getter_AddRefs(resultNode)); + if (NS_FAILED(res)) return res; + } + // Use transaction system to insert the entire row+cells + // (Note that rows are inserted at same childoffset each time) + res = nsEditor::InsertNode(newRow, parentOfRow, newRowOffset); + if (NS_FAILED(res)) return res; + } + } + } + return res; +} + +// Editor helper only +NS_IMETHODIMP +nsHTMLEditor::DeleteTable(nsCOMPtr &aTable, nsCOMPtr &aSelection) +{ + nsCOMPtr tableParent; + PRInt32 tableOffset; + if(NS_FAILED(aTable->GetParentNode(getter_AddRefs(tableParent))) || !tableParent) + return NS_ERROR_FAILURE; + + // Save offset we need to restore the selection + if(NS_FAILED(GetChildOffset(aTable, tableParent, tableOffset))) + return NS_ERROR_FAILURE; + + nsresult res = DeleteNode(aTable); + + // Place selection just before the table + aSelection->Collapse(tableParent, tableOffset); + + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::DeleteTable() +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr cell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex; + nsresult res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); + + if (NS_SUCCEEDED(res)) + { + nsAutoEditBatch beginBatching(this); + res = DeleteTable(table, selection); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::DeleteTableCell(PRInt32 aNumber) +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr cell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex; + + nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + + nsAutoEditBatch beginBatching(this); + + for (PRInt32 i = 0; i < aNumber; i++) + { + res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); + if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + if (1 == GetNumberOfCellsInRow(table, startRowIndex)) + { + nsCOMPtr parentRow; + res = GetElementOrParentByTagName("tr", cell, getter_AddRefs(parentRow)); + if (NS_FAILED(res)) return res; + if (!parentRow) return NS_ERROR_NULL_POINTER; + + // We should delete the row instead, + // but first check if its the only row left + // so we can delete the entire table + PRInt32 rowCount, colCount; + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + if (rowCount == 1) + return DeleteTable(table, selection); + + // We need to call DeleteTableRow to handle cells with rowspan + res = DeleteTableRow(1); + if (NS_FAILED(res)) return res; + } + else + { + // More than 1 cell in the row + + // We clear the selection to avoid problems when nodes in the selection are deleted, + // The setCaret object will call SetCaretAfterTableEdit in it's destructor + selection->ClearSelection(); + nsSetCaretAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn); + nsAutoTxnsConserveSelection dontChangeSelection(this); + + res = DeleteNode(cell); + // If we fail, don't try to delete any more cells??? + if (NS_FAILED(res)) return res; + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::DeleteTableCellContents() +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr cell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex; + nsresult res = NS_OK; + + res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); + if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + // We clear the selection to avoid problems when nodes in the selection are deleted, + selection->ClearSelection(); + nsSetCaretAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn); + //Don't let Rules System change the selection + nsAutoTxnsConserveSelection dontChangeSelection(this); + + nsAutoEditBatch beginBatching(this); + + nsCOMPtr nodeList; + res = cell->GetChildNodes(getter_AddRefs(nodeList)); + if (NS_FAILED(res)) return res; + + if (!nodeList) return NS_ERROR_FAILURE; + PRUint32 nodeListLength; + res = nodeList->GetLength(&nodeListLength); + if (NS_FAILED(res)) return res; + + for (PRUint32 i = 0; i < nodeListLength; i++) + { + + nsCOMPtr child; + res = cell->GetLastChild(getter_AddRefs(child)); + if (NS_FAILED(res)) return res; + res = DeleteNode(child); + if (NS_FAILED(res)) return res; + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::DeleteTableColumn(PRInt32 aNumber) +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr cell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex; + PRInt32 rowCount, colCount; + nsresult res = NS_OK; + + res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); + if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + // Shortcut the case of deleting all columns in table + if(startColIndex == 0 && aNumber >= colCount) + return DeleteTable(table, selection); + + nsAutoEditBatch beginBatching(this); + + // Check for counts too high + aNumber = PR_MIN(aNumber,(colCount-startColIndex)); + + // Scan through cells in row to do rowspan adjustments + nsCOMPtr curCell; + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + PRInt32 rowIndex = 0; + + for (PRInt32 i = 0; i < aNumber; i++) + { + do { + res = GetCellDataAt(table, rowIndex, startColIndex, *getter_AddRefs(curCell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + + if (NS_FAILED(res)) return res; + + if (curCell) + { + // This must always be >= 1 + NS_ASSERTION((actualRowSpan > 0),"Effective ROWSPAN = 0 in DeleteTableColumn"); + + // Find cells that don't start in column we are deleting + if (curStartColIndex < startColIndex || colSpan > 1 || colSpan == 0) + { + // We have a cell spanning this location + // Decrease its colspan to keep table rectangular, + // but if colSpan=0, it will adjust automatically + if (colSpan > 0) + { + NS_ASSERTION((colSpan > 1),"Bad COLSPAN in DeleteTableColumn"); + SetColSpan(curCell,colSpan-1); + } + if (curStartColIndex == startColIndex) + { + // Cell is in column to be deleted, + // but delete contents of cell instead of cell itself + selection->Collapse(curCell,0); + DeleteTableCellContents(); + } + // To next cell in column + rowIndex += actualRowSpan; + } else { + // Delete the cell + if (1 == GetNumberOfCellsInRow(table, rowIndex)) + { + // Only 1 cell in row - delete the row + nsCOMPtr parentRow; + res = GetElementOrParentByTagName("tr", cell, getter_AddRefs(parentRow)); + if (NS_FAILED(res)) return res; + if(!parentRow) return NS_ERROR_NULL_POINTER; + + // But first check if its the only row left + // so we can delete the entire table + // (This should never happen but it's the safe thing to do) + PRInt32 rowCount, colCount; + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + if (rowCount == 1) + return DeleteTable(table, selection); + + // Delete the row by placing caret in cell we were to delete + // We need to call DeleteTableRow to handle cells with rowspan + selection->Collapse(cell,0); + res = DeleteTableRow(1); + if (NS_FAILED(res)) return res; + + // Note that we don't incremenet rowIndex + // since a row was deleted and "next" + // row now has current rowIndex + } else { + + selection->ClearSelection(); + nsSetCaretAfterTableEdit setCaret(this, table, curStartRowIndex, curStartColIndex, ePreviousColumn); + //Don't let Rules System change the selection + nsAutoTxnsConserveSelection dontChangeSelection(this); + + res = DeleteNode(curCell); + if (NS_FAILED(res)) return res; + + //Skipover any rows spanned by this cell + rowIndex += actualRowSpan; + } + } + } + } while (curCell); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::DeleteTableRow(PRInt32 aNumber) +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr cell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex; + PRInt32 rowCount, colCount; + nsresult res = NS_OK; + + res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); + if (NS_FAILED(res)) return res; + // Don't fail if no cell found + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + // Shortcut the case of deleting all rows in table + if(startRowIndex == 0 && aNumber >= rowCount) + return DeleteTable(table, selection); + + nsAutoEditBatch beginBatching(this); + + // Check for counts too high + aNumber = PR_MIN(aNumber,(rowCount-startRowIndex)); + + // We clear the selection to avoid problems when nodes in the selection are deleted, + // Be sure to set it correctly later (in SetCaretAfterTableEdit)! + selection->ClearSelection(); + + // Scan through cells in row to do rowspan adjustments + nsCOMPtr curCell; + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + PRInt32 colIndex = 0; + do { + res = GetCellDataAt(table, startRowIndex, colIndex, *getter_AddRefs(curCell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + + // We don't fail if we don't find a cell, so this must be real bad + if(NS_FAILED(res)) return res; + + // Find cells that don't start in row we are deleting + if (curCell) + { + //Real colspan must always be >= 1 + NS_ASSERTION((actualColSpan > 0),"Effective COLSPAN = 0 in DeleteTableRow"); + if (curStartRowIndex < startRowIndex) + { + // We have a cell spanning this location + // Decrease its rowspan to keep table rectangular + // but we don't need to do this if rowspan=0, + // since it will automatically adjust + if (rowSpan > 0) + SetRowSpan(curCell, PR_MAX((startRowIndex - curStartRowIndex), actualRowSpan - aNumber)); + } + // Skip over locations spanned by this cell + colIndex += actualColSpan; + } + } while (curCell); + + for (PRInt32 i = 0; i < aNumber; i++) + { + //TODO: To minimize effect of deleting cells that have rowspan > 1: + // Scan for rowspan > 1 and insert extra emtpy cells in + // appropriate rows to take place of spanned regions. + // (Hard part is finding appropriate neighbor cell before/after in correct row) + + // Delete the row + nsCOMPtr parentRow; + res = GetElementOrParentByTagName("tr", cell, getter_AddRefs(parentRow)); + if (NS_SUCCEEDED(res) && parentRow) + res = DeleteNode(parentRow); + if (NS_FAILED(res)) + startRowIndex++; + + res = GetCellAt(table, startRowIndex, startColIndex, *getter_AddRefs(cell)); + if(!cell) + break; + } + return res; +} + + + +NS_IMETHODIMP +nsHTMLEditor::SelectTable() +{ + nsCOMPtr table; + nsresult res = NS_ERROR_FAILURE; + res = GetElementOrParentByTagName("table", nsnull, getter_AddRefs(table)); + if (NS_FAILED(res)) return res; + // Don't fail if we didn't find a table + if (!table) return NS_OK; + + nsCOMPtr tableNode = do_QueryInterface(table); + if (tableNode) + { + res = ClearSelection(); + if (NS_SUCCEEDED(res)) + res = AppendNodeToSelectionAsRange(table); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::SelectTableCell() +{ + nsCOMPtr cell; + nsresult res = NS_ERROR_FAILURE; + res = GetElementOrParentByTagName("td", nsnull, getter_AddRefs(cell)); + if (NS_FAILED(res)) return res; + // Don't fail if we didn't find a table + if (!cell) return NS_EDITOR_ELEMENT_NOT_FOUND; + + nsCOMPtr cellNode = do_QueryInterface(cell); + if (cellNode) + { + res = ClearSelection(); + if (NS_SUCCEEDED(res)) + res = AppendNodeToSelectionAsRange(cellNode); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::SelectAllTableCells() +{ + nsresult res = NS_OK; + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::SelectTableRow() +{ + nsresult res = NS_OK; + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::SelectTableColumn() +{ + nsresult res = NS_OK; + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::JoinTableCells() +{ + nsCOMPtr selection; + nsCOMPtr table; + nsCOMPtr cell; + nsCOMPtr cellParent; + PRInt32 cellOffset, startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + + PRInt32 rowCount, colCount; + nsresult res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + res = GetCellContext(selection, table, cell, cellParent, cellOffset, startRowIndex, startColIndex); + if (NS_FAILED(res)) return res; + if(!cell) return NS_ERROR_NULL_POINTER; + + res = GetCellDataAt(table, startRowIndex, startColIndex, *getter_AddRefs(cell), + startRowIndex, startColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + if (NS_FAILED(res)) return res; + if(!cell) return NS_ERROR_NULL_POINTER; + + //*** Initial test: just merge with cell to the right + + nsCOMPtr cell2; + PRInt32 startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2; + PRBool isSelected2; + res = GetCellDataAt(table, startRowIndex, startColIndex+actualColSpan, *getter_AddRefs(cell2), + startRowIndex2, startColIndex2, rowSpan2, colSpan2, + actualRowSpan2, actualColSpan2, isSelected2); + if (NS_FAILED(res)) return res; + + nsAutoEditBatch beginBatching(this); + //Don't let Rules System change the selection + nsAutoTxnsConserveSelection dontChangeSelection(this); + + nsCOMPtr parentNode = do_QueryInterface(cellParent); + nsCOMPtr cellNode = do_QueryInterface(cell); + nsCOMPtr cellNode2 = do_QueryInterface(cell2); + if(cellNode && cellNode2 && parentNode) + { + PRInt32 insertIndex = 0; + + // Get index of last child in target cell + nsCOMPtr childNodes; + res = cellNode->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(res)) && (childNodes)) + { + // Start inserting just after last child + PRUint32 len; + res = childNodes->GetLength(&len); + if (NS_FAILED(res)) return res; + insertIndex = (PRInt32)len; + } + + // Move content from cell2 to cell + nsCOMPtr cellChild; + res = cell2->GetFirstChild(getter_AddRefs(cellChild)); + if (NS_FAILED(res)) return res; + while (cellChild) + { + nsCOMPtr nextChild; + res = cellChild->GetNextSibling(getter_AddRefs(nextChild)); + if (NS_FAILED(res)) return res; + + res = DeleteNode(cellChild); + if (NS_FAILED(res)) return res; + + res = nsEditor::InsertNode(cellChild, cellNode, insertIndex); + if (NS_FAILED(res)) return res; + + cellChild = nextChild; + insertIndex++; + } + // Reset target cell's spans + res = SetColSpan(cell, actualColSpan+actualColSpan2); + if (NS_FAILED(res)) return res; + + // Delete cells whose contents were moved + res = DeleteNode(cell2); + if (NS_FAILED(res)) return res; + + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::NormalizeTable(nsIDOMElement *aTable) +{ + nsCOMPtr table; + nsresult res = NS_ERROR_FAILURE; + res = GetElementOrParentByTagName("table", aTable, getter_AddRefs(table)); + if (NS_FAILED(res)) return res; + // Don't fail if we didn't find a table + if (!table) return NS_OK; + + PRInt32 rowCount, colCount, rowIndex, colIndex; + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + nsAutoEditBatch beginBatching(this); + + nsCOMPtr cell; + PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + +#if 0 +// This isn't working yet -- layout errors contribute! + + // First scan all cells in row to detect bad rowspan values + for(rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + PRInt32 minRowSpan = 0x7fffffff; //XXX: Shouldn't there be a define somewhere for MaxInt for PRInt32 + for(colIndex = 0; colIndex < colCount; colIndex++) + { + res = GetCellDataAt(aTable, rowIndex, colIndex, *getter_AddRefs(cell), + startRowIndex, startColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + // NOTE: This is a *real* failure. + // GetCellDataAt passes if cell is missing from cellmap + if(NS_FAILED(res)) return res; + if(cell && rowSpan > 0 && rowSpan < minRowSpan) + minRowSpan = rowSpan; + } + if(minRowSpan > 1) + { + PRInt32 spanDiff = minRowSpan - 1; + for(colIndex = 0; colIndex < colCount; colIndex++) + { + res = GetCellDataAt(aTable, rowIndex, colIndex, *getter_AddRefs(cell), + startRowIndex, startColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + // NOTE: This is a *real* failure. + // GetCellDataAt passes if cell is missing from cellmap + if(NS_FAILED(res)) return res; + // Fixup rowspans for cells starting in current row + if(cell && rowSpan > 0 && + startRowIndex == rowIndex && + startColIndex == colIndex ) + { + // Set rowspan so there's at least one cell with ROWSPAN=1 + SetRowSpan(cell, rowSpan-spanDiff); + } + } + } + } + // Get Table size again in case above changed anything + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; +#endif + + // Fill in missing cellmap locations with empty cells + for(rowIndex = 0; rowIndex < rowCount; rowIndex++) + { + nsCOMPtr previousCellInRow; + + for(colIndex = 0; colIndex < colCount; colIndex++) + { + res = GetCellDataAt(aTable, rowIndex, colIndex, *getter_AddRefs(cell), + startRowIndex, startColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + // NOTE: This is a *real* failure. + // GetCellDataAt passes if cell is missing from cellmap + if(NS_FAILED(res)) return res; + if (!cell) + { + //We are missing a cell at a cellmap location +#ifdef DEBUG + printf("NormalizeTable found missing cell at row=%d, col=%d\n", rowIndex, colIndex); +#endif + // Add a cell after the previous Cell in the current row + if(previousCellInRow) + { + // Insert a new cell after (PR_TRUE), and return the new cell to us + res = InsertCell(previousCellInRow, 1, 1, PR_TRUE, getter_AddRefs(cell)); + if (NS_FAILED(res)) return res; + + // Set this so we use returned new "cell" to set previousCellInRow below + if(cell) + startRowIndex = rowIndex; + } else { + // We don't have any cells in this row -- We are really messed up! +#ifdef DEBUG + printf("NormalizeTable found no cells in row=%d, col=%d\n", rowIndex, colIndex); +#endif + return NS_ERROR_FAILURE; + } + } + // Save the last cell found in the same row we are scanning + if(startRowIndex == rowIndex) + { + previousCellInRow = cell; + } + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetCellIndexes(nsIDOMElement *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex) +{ + nsresult res=NS_ERROR_NOT_INITIALIZED; + aColIndex=0; // initialize out params + aRowIndex=0; + if (!aCell) + { + // Get the selected cell or the cell enclosing the selection anchor + nsCOMPtr cell; + res = GetElementOrParentByTagName("td", nsnull, getter_AddRefs(cell)); + if (NS_SUCCEEDED(res) && cell) + aCell = cell; + else + return NS_ERROR_FAILURE; + } + + res = NS_ERROR_FAILURE; // we return an error unless we get the index + nsISupports *layoutObject=nsnull; // frames are not ref counted, so don't use an nsCOMPtr + + res = nsHTMLEditor::GetLayoutObject(aCell, &layoutObject); + + if ((NS_SUCCEEDED(res)) && (nsnull!=layoutObject)) + { // get the table cell interface from the frame + nsITableCellLayout *cellLayoutObject=nsnull; // again, frames are not ref-counted + + res = layoutObject->QueryInterface(NS_GET_IID(nsITableCellLayout), (void**)(&cellLayoutObject)); + if ((NS_SUCCEEDED(res)) && (nsnull!=cellLayoutObject)) + { + res = cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex); + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **tableLayoutObject) +{ + *tableLayoutObject=nsnull; + if (!aTable) + return NS_ERROR_NOT_INITIALIZED; + + // frames are not ref counted, so don't use an nsCOMPtr + nsISupports *layoutObject=nsnull; + nsresult res = nsHTMLEditor::GetLayoutObject(aTable, &layoutObject); + if ((NS_SUCCEEDED(res)) && (nsnull!=layoutObject)) + { // get the table interface from the frame + + res = layoutObject->QueryInterface(NS_GET_IID(nsITableLayout), + (void**)(tableLayoutObject)); + } + return res; +} + +//Return actual number of cells (a cell with colspan > 1 counts as just 1) +PRBool nsHTMLEditor::GetNumberOfCellsInRow(nsIDOMElement* aTable, PRInt32 rowIndex) +{ + PRInt32 cellCount = 0; + nsCOMPtr cell; + PRInt32 colIndex = 0; + nsresult res; + do { + PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + res = GetCellDataAt(aTable, rowIndex, colIndex, *getter_AddRefs(cell), + startRowIndex, startColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + if (NS_FAILED(res)) return res; + if (cell) + { + // Only count cells that start in row we are working with + if (startRowIndex == rowIndex) + cellCount++; + + //Next possible location for a cell + colIndex += actualColSpan; + } + else + colIndex++; + + } while (cell); + + return cellCount; +} + +/* Not scriptable: For convenience in C++ + Use GetTableRowCount and GetTableColumnCount from JavaScript +*/ +NS_IMETHODIMP +nsHTMLEditor::GetTableSize(nsIDOMElement *aTable, PRInt32& aRowCount, PRInt32& aColCount) +{ + nsresult res = NS_ERROR_FAILURE; + aRowCount = 0; + aColCount = 0; + nsCOMPtr table; + // Get the selected talbe or the table enclosing the selection anchor + res = GetElementOrParentByTagName("table", aTable, getter_AddRefs(table)); + if (NS_FAILED(res)) return res; + if (!table) return NS_ERROR_FAILURE; + + // frames are not ref counted, so don't use an nsCOMPtr + nsITableLayout *tableLayoutObject; + res = GetTableLayoutObject(table.get(), &tableLayoutObject); + if (NS_FAILED(res)) return res; + if (!tableLayoutObject) + return NS_ERROR_FAILURE; + + return tableLayoutObject->GetTableSize(aRowCount, aColCount); +} + +NS_IMETHODIMP +nsHTMLEditor::GetCellDataAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell, + PRInt32& aStartRowIndex, PRInt32& aStartColIndex, + PRInt32& aRowSpan, PRInt32& aColSpan, + PRInt32& aActualRowSpan, PRInt32& aActualColSpan, + PRBool& aIsSelected) +{ + nsresult res=NS_ERROR_FAILURE; + aCell = nsnull; + aStartRowIndex = 0; + aStartColIndex = 0; + aRowSpan = 0; + aColSpan = 0; + aActualRowSpan = 0; + aActualColSpan = 0; + aIsSelected = PR_FALSE; + + if (!aTable) + { + // Get the selected table or the table enclosing the selection anchor + nsCOMPtr table; + res = GetElementOrParentByTagName("table", nsnull, getter_AddRefs(table)); + if (NS_FAILED(res)) return res; + if (table) + aTable = table; + else + return NS_ERROR_FAILURE; + } + + // frames are not ref counted, so don't use an nsCOMPtr + nsITableLayout *tableLayoutObject; + res = GetTableLayoutObject(aTable, &tableLayoutObject); + if (NS_FAILED(res)) return res; + if (!tableLayoutObject) return NS_ERROR_FAILURE; + + // Note that this returns NS_TABLELAYOUT_CELL_NOT_FOUND when + // the index(es) are out of bounds + res = tableLayoutObject->GetCellDataAt(aRowIndex, aColIndex, aCell, + aStartRowIndex, aStartColIndex, + aRowSpan, aColSpan, + aActualRowSpan, aActualColSpan, + aIsSelected); + // Convert to editor's generic "not found" return value + if (res == NS_TABLELAYOUT_CELL_NOT_FOUND) res = NS_EDITOR_ELEMENT_NOT_FOUND; + return res; +} + +// When all you want is the cell +NS_IMETHODIMP +nsHTMLEditor::GetCellAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell) +{ + PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + return GetCellDataAt(aTable, aRowIndex, aColIndex, aCell, + startRowIndex, startColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); +} + +NS_IMETHODIMP +nsHTMLEditor::GetCellContext(nsCOMPtr &aSelection, + nsCOMPtr &aTable, nsCOMPtr &aCell, + nsCOMPtr &aCellParent, PRInt32& aCellOffset, + PRInt32& aRow, PRInt32& aCol) +{ + nsresult res = nsEditor::GetSelection(getter_AddRefs(aSelection)); + if (NS_FAILED(res)) return res; + if (!aSelection) return NS_ERROR_FAILURE; + + // Find the first selected cell + res = GetFirstSelectedCell(getter_AddRefs(aCell)); + if (!aCell) + { + //If a cell wasn't selected, then assume the selection is INSIDE + // and use anchor node to search up to the containing cell + nsCOMPtr anchorNode; + + res = aSelection->GetAnchorNode(getter_AddRefs(anchorNode)); + if (NS_FAILED(res)) return res; + if (!anchorNode) return NS_ERROR_FAILURE; + + // Get the cell enclosing the selection anchor + res = GetElementOrParentByTagName("td", anchorNode, getter_AddRefs(aCell)); + if (NS_FAILED(res)) return res; + if (!aCell) return NS_ERROR_FAILURE; + } + // Get containing table and the immediate parent of the cell + res = GetElementOrParentByTagName("table", aCell, getter_AddRefs(aTable)); + if (NS_FAILED(res)) return res; + if (!aTable) return NS_ERROR_FAILURE; + + res = aCell->GetParentNode(getter_AddRefs(aCellParent)); + if (NS_FAILED(res)) return res; + if (!aCellParent) return NS_ERROR_FAILURE; + + // Get current cell location so we can put caret back there when done + res = GetCellIndexes(aCell, aRow, aCol); + if(NS_FAILED(res)) return res; + + // And the parent and offsets needed to do an insert + return GetChildOffset(aCell, aCellParent, aCellOffset); +} + +NS_IMETHODIMP +nsHTMLEditor::GetFirstSelectedCell(nsIDOMElement **aCell) +{ + if (!aCell) return NS_ERROR_NULL_POINTER; + *aCell = nsnull; + + nsCOMPtr selection; + nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + +//TODO: Replace this with code below new "table cell mode" flag is implemented + + nsCOMPtr enumerator; + res = selection->GetEnumerator(getter_AddRefs(enumerator)); + + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + enumerator->First(); + nsCOMPtr currentItem; + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(res)) && currentItem) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + iter->Init(range); + // loop through the content iterator for each content node + nsCOMPtr content; + + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + // Not likely! + if (NS_FAILED(res)) return NS_ERROR_FAILURE; + + nsCOMPtr atom; + content->GetTag(*getter_AddRefs(atom)); + if (atom.get() == nsIEditProperty::td || + atom.get() == nsIEditProperty::th ) + { + // We found a cell + nsCOMPtr cellElement = do_QueryInterface(content); + if (cellElement) + { + *aCell = cellElement.get(); + NS_ADDREF(*aCell); + } + return NS_OK; + } + iter->Next(); + } + } + return NS_EDITOR_ELEMENT_NOT_FOUND; + +#if 0 +//TODO: Do this only after checking the new "table cell mode" flag + // The first cell is the starting node in the first selection range + nsCOMPtr firstRange; + res = selection->GetRangeAt(0, getter_AddRefs(firstRange)); + if (NS_FAILED(res)) return res; + if (!firstRange) return NS_ERROR_FAILURE; + + nsCOMPtr cellNode; + res = GetFirstNodeInRange(firstRange, getter_AddRefs(cellNode)); + if (NS_FAILED(res)) return res; + if (!cellNode) return NS_ERROR_FAILURE; + nsCOMPtr cellElement = do_QueryInterface(cellNode); + + if (cellElement) + { + *aCell = cellElement.get(); + NS_ADDREF(*aCell); + } + else res = NS_EDITOR_ELEMENT_NOT_FOUND; + + return res; +#endif +} + +NS_IMETHODIMP +nsHTMLEditor::SetCaretAfterTableEdit(nsIDOMElement* aTable, PRInt32 aRow, PRInt32 aCol, PRInt32 aDirection) +{ + nsresult res = NS_ERROR_NOT_INITIALIZED; + if (!aTable) return res; + + nsCOMPtrselection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + if (!selection) + { +#ifdef DEBUG_cmanske + printf("Selection not found after table manipulation!\n"); +#endif + return NS_ERROR_FAILURE; + } + + nsCOMPtr cell; + PRBool done = PR_FALSE; + do { + res = GetCellAt(aTable, aRow, aCol, *getter_AddRefs(cell)); + nsCOMPtr cellNode = do_QueryInterface(cell); + if (NS_SUCCEEDED(res)) + { + if (cell) + { + // Set the caret to just before the first child of the cell? + // TODO: Should we really be placing the caret at the END + // of the cell content? + selection->Collapse(cell, 0); + return NS_OK; + } else { + // Setup index to find another cell in the + // direction requested, but move in + // other direction if already at beginning of row or column + switch (aDirection) + { + case ePreviousColumn: + if (aCol == 0) + { + if (aRow > 0) + aRow--; + else + done = PR_TRUE; + } + else + aCol--; + break; + case ePreviousRow: + if (aRow == 0) + { + if (aCol > 0) + aCol--; + else + done = PR_TRUE; + } + else + aRow--; + break; + default: + done = PR_TRUE; + } + } + } + else + break; + } while (!done); + + // We didn't find a cell + // Set selection to just before the table + nsCOMPtr tableParent; + PRInt32 tableOffset; + res = aTable->GetParentNode(getter_AddRefs(tableParent)); + if(NS_SUCCEEDED(res) && tableParent) + { + if(NS_SUCCEEDED(GetChildOffset(aTable, tableParent, tableOffset))) + return selection->Collapse(tableParent, tableOffset); + } + // Last resort: Set selection to start of doc + // (it's very bad to not have a valid selection!) + return SetSelectionAtDocumentStart(selection); +} + +NS_IMETHODIMP +nsHTMLEditor::GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRBool &aIsSelected) +{ + aTableElement = nsnull; + aTagName = ""; + aIsSelected = PR_FALSE; + + nsCOMPtr selection; + nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + + nsAutoString tableName("table"); + nsAutoString trName("tr"); + nsAutoString tdName("td"); + + nsCOMPtr anchorNode; + + // Find the first selected cell + // TODO: Handle multiple cells selected! + nsCOMPtr firstCell; + + nsCOMPtr tableElement; + res = GetFirstSelectedCell(getter_AddRefs(tableElement)); + if(NS_FAILED(res)) return res; + if (tableElement) + { + aTagName = tdName; + aIsSelected = PR_TRUE; + goto SET_RETURN_ELEMENT; + } + + // See if table or row is selected + res = GetSelectedElement(tableName, getter_AddRefs(tableElement)); + if(NS_FAILED(res)) return res; + if (tableElement) + { + aTagName = tableName; + aIsSelected = PR_TRUE; + goto SET_RETURN_ELEMENT; + } + res = GetSelectedElement(trName, getter_AddRefs(tableElement)); + if(NS_FAILED(res)) return res; + if (tableElement) + { + aTagName = trName; + aIsSelected = PR_TRUE; + goto SET_RETURN_ELEMENT; + } + + // Look for a table cell parent + res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); + if (NS_FAILED(res)) return res; + if (!anchorNode) return NS_ERROR_FAILURE; + + res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableElement)); + if(NS_FAILED(res)) return res; + if (tableElement) + { + aTagName = tdName; +SET_RETURN_ELEMENT: + aTableElement = tableElement.get(); + NS_ADDREF(aTableElement); + } + return res; +} diff --git a/editor/public/nsIHTMLEditor.h b/editor/public/nsIHTMLEditor.h index cc257f9baec9..c0720bb912e0 100644 --- a/editor/public/nsIHTMLEditor.h +++ b/editor/public/nsIHTMLEditor.h @@ -30,6 +30,8 @@ 0x4805e683, 0x49b9, 0x11d3, \ { 0x9c, 0xe4, 0xed, 0x60, 0xbd, 0x6c, 0xb5, 0xbc } } +#define NS_EDITOR_ELEMENT_NOT_FOUND \ + NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_EDITOR, 1) class nsString; class nsStringArray; @@ -297,6 +299,7 @@ public: * * @param aNode The node in the document to start the search * If it is null, the anchor node of the current selection is used + * Returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found (passes NS_SUCCEEDED macro) */ NS_IMETHOD GetElementOrParentByTagName(const nsString& aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn)=0; @@ -314,6 +317,7 @@ public: * (an "A" tag with the "href" attribute set) * Use "anchor" or "namedanchor" to get a named anchor node * (an "A" tag with the "name" attribute set) + * Returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found (passes NS_SUCCEEDED macro) */ NS_IMETHOD GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn)=0; diff --git a/editor/public/nsITableEditor.h b/editor/public/nsITableEditor.h index 86355b6ca432..3d5cbc118e9c 100644 --- a/editor/public/nsITableEditor.h +++ b/editor/public/nsITableEditor.h @@ -77,6 +77,16 @@ public: NS_IMETHOD DeleteTableColumn(PRInt32 aNumber)=0; NS_IMETHOD DeleteTableRow(PRInt32 aNumber)=0; + /** Table Selection methods + * Selecting a row or column actually + * selects all cells (not TR in the case of rows) + */ + NS_IMETHOD SelectTableCell()=0; + NS_IMETHOD SelectTableRow()=0; + NS_IMETHOD SelectTableColumn()=0; + NS_IMETHOD SelectTable()=0; + NS_IMETHOD SelectAllTableCells()=0; + /** Join the contents of the selected cells into one cell, * expanding that cells ROWSPAN and COLSPAN to take up * the same number of cellmap locations as before. @@ -119,10 +129,8 @@ public: * @param aTable A table in the document * @param aRowIndex, aColIndex The 0-based cellmap indexes * - * Note that this returns NS_TABLELAYOUT_CELL_NOT_FOUND - * when a cell is not found at the given indexes, - * but this passes the NS_SUCCEEDED() test, - * so you can scan for all cells in a row or column + * Returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found (passes NS_SUCCEEDED macro) + * You can scan for all cells in a row or column * by iterating through the appropriate indexes * until the returned aCell is null */ @@ -150,8 +158,7 @@ public: * @param aIsSelected * @param * - * Note that this returns NS_TABLELAYOUT_CELL_NOT_FOUND - * when a cell is not found at the given indexes (see note for GetCellAt()) + * Returns NS_EDITOR_ELEMENT_NOT_FOUND if a cell is not found (passes NS_SUCCEEDED macro) */ NS_IMETHOD GetCellDataAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell, PRInt32& aStartRowIndex, PRInt32& aStartColIndex, @@ -167,6 +174,7 @@ public: * Returns: * @param aRow The row at the requested index * Returns null if there are no rows in table + * Returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found (passes NS_SUCCEEDED macro) */ NS_IMETHOD GetFirstRow(nsIDOMElement* aTableElement, nsIDOMElement* &aRow)=0; @@ -178,6 +186,7 @@ public: * @param aRow The row to start search from * and the row returned from the search * Returns null if there isn't another row + * Returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found (passes NS_SUCCEEDED macro) */ NS_IMETHOD GetNextRow(nsIDOMElement* aTableElement, nsIDOMElement* &aRow)=0; @@ -219,6 +228,7 @@ public: * Note that "td" will be returned if name is actually "th" * @param aIsSelected Tells if element returned is a selected element * (false if element is a parent cell of selection) + * Returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found (passes NS_SUCCEEDED macro) */ NS_IMETHOD GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRBool &aIsSelected)=0; diff --git a/editor/ui/composer/content/EditorCommands.js b/editor/ui/composer/content/EditorCommands.js index 1c81bed69733..b8c0ddcfc048 100644 --- a/editor/ui/composer/content/EditorCommands.js +++ b/editor/ui/composer/content/EditorCommands.js @@ -27,7 +27,9 @@ var editorShell; var documentModified; +var prefAuthorString = ""; var EditorDisplayMode = 0; // Normal Editor mode +var WebCompose = false; // Set true for Web Composer, leave false for Messenger Composer // These must be kept in synch with the XUL lists var gParagraphTagNames = new Array("","P","H1","H2","H3","H4","H5","H6","BLOCKQUOTE","ADDRESS","PRE","DT","DD"); @@ -49,6 +51,14 @@ function EditorOnLoad() { } // Continue with normal startup. EditorStartup( 'html', document.getElementById("content-frame")); + + // Active menu items that are initially hidden in XUL + // because they are not needed by Messenger Composer + var styleMenu = document.getElementById("stylesheetMenu") + if (styleMenu) + styleMenu.removeAttribute("hidden"); + + WebCompose = true; window.tryToClose = EditorClose; } @@ -67,9 +77,12 @@ function TextEditorOnLoad() { var DocumentStateListener = { NotifyDocumentCreated: function() - { - EditorSetDefaultPrefs(); - }, + { + EditorSetDefaultPrefs(); + // Call EditorSetDefaultPrefs first + // so it gets the default author before initing toolbars + EditorInitToolbars(); + }, NotifyDocumentWillBeDestroyed: function() {}, NotifyDocumentStateChanged:function( isNowDirty ) {} }; @@ -79,11 +92,6 @@ function EditorStartup(editorType, editorElement) dump("Doing Editor Startup...\n"); contentWindow = window.content; - // set up event listeners - window.addEventListener("load", EditorDocumentLoaded, true, false); - - dump("Trying to make an Editor Shell through the component manager...\n"); - // store the editor shell in the window, so that child windows can get to it. var editorShell = window.editorShell = editorElement.editorShell; @@ -278,8 +286,13 @@ function EditorOpen() filePicker = filePicker.QueryInterface(Components.interfaces.nsIFileSpecWithUI); /* doesn't handle *.shtml files */ - filePicker.chooseInputFile(editorShell.GetString("OpenHTMLFile"), filePicker.eHTMLFiles+filePicker.eTextFiles+filePicker.eAllFiles, 0, 0); - /* need to handle cancel (uncaught exception at present) */ + try { + filePicker.chooseInputFile(editorShell.GetString("OpenHTMLFile"), filePicker.eHTMLFiles+filePicker.eTextFiles+filePicker.eAllFiles, 0, 0); + /* need to handle cancel (uncaught exception at present) */ + } + catch (ex) { + dump("filePicker.chooseInputFile threw an exception\n"); + } /* check for already open window and activate it... */ var found = FindAndSelectEditorWindowWithURL(filePicker.URLString); @@ -927,20 +940,46 @@ function EditorSetDisplayStyle(mode) { EditorDisplayMode = mode; editorShell.SetDisplayMode(mode); - editButton = document.getElementById("EditModeButton"); - browserButton = document.getElementById("BrowserModeButton"); + var editButton = document.getElementById("EditModeButton"); + var browserButton = document.getElementById("BrowserModeButton"); + var showMenu = document.getElementById("ShowExtraMarkup"); + var hideMenu = document.getElementById("HideExtraMarkup"); + var editSelected = 0; var browserSelected = 0; - if (mode == 0) editSelected = 1; - if (mode == 1) browserSelected = 1; - dump(editButton+browserButton+" Display mode: EditSelected="+editSelected+" BrowserSelected="+browserSelected+"\n"); + if (mode == 0) + { + editSelected = 1; + showMenu.setAttribute("hidden","true"); + hideMenu.removeAttribute("hidden"); + } + if (mode == 1) + { + browserSelected = 1; + showMenu.removeAttribute("hidden"); + hideMenu.setAttribute("hidden","true"); + } editButton.setAttribute("selected",Number(editSelected)); browserButton.setAttribute("selected",Number(browserSelected)); + contentWindow.focus(); +} + +function EditorPreview() +{ + contentWindow.focus(); +} + +function EditorPrintSetup() +{ + // Old code? Browser no longer is doing this + //window.openDialog("resource:/res/samples/printsetup.html", "_blank", "chrome,close,titlebar", ""); + _EditorNotImplemented(); + contentWindow.focus(); } function EditorPrintPreview() { - window.openDialog("resource:/res/samples/printsetup.html", "_blank", "chrome,close,titlebar", ""); + _EditorNotImplemented(); contentWindow.focus(); } @@ -977,11 +1016,80 @@ function CheckSpelling() contentWindow.focus(); } -function OnCreateAlignmentPopup() +function EditorInitEditMenu() { - dump("Creating Alignment popup window\n"); -} +} + +function EditorInitFormatMenu() +{ + var propertiesMenu = document.getElementById("objectProperties"); + if (propertiesMenu) + { + var element = editorShell.GetSelectedElement(""); + dump("EditorObjectProperties: element="+element+"\n"); + if (element) + { + dump("TagName="+element.nodeName+"\n"); + if (element.nodeName) + { + propertiesMenu.removeAttribute("hidden"); + var objStr; + var menuStr = editorShell.GetString("ObjectProperties"); + switch (element.nodeName) + { + case 'IMG': + objStr = "Image"; + break; + case 'HR': + objStr = "H.Line"; + break; + case 'TABLE': + objStr = "Table"; + break; + case 'A': + if(element.href) + { + objStr = "Link"; + EditorInsertOrEditLink(); + } else if (element.name) + { + objStr = "Named Anchor"; + EditorInsertOrEditNamedAnchor(); + } + break; + } + menuStr = menuStr.replace(/%obj%/,objStr); + propertiesMenu.setAttribute("value", menuStr) + } else { + propertiesMenu.setAttribute("hidden","true"); + } + } + } + dump("Calling EditorInitViewMenu()\n"); +} + +function EditorInitToolbars() +{ + // Set title edit field + var domdoc; + try { domdoc = window.editorShell.editorDocument; } catch (e) { dump( e + "\n"); } + if ( !domdoc ) + { + dump("EditorInitToolbars: EDITOR DOCUMENT NOT FOUND\n"); + return; + } + var title = domdoc.title; + var titleInput = document.getElementById("PageTitleInput"); + if (!title) title = ""; + titleInput.setAttribute("value", title); + + var authorInput = document.getElementById("AuthorInput"); + if (authorInput) + { + } +} + function EditorSetDefaultPrefs() { /* only set defaults for new documents */ @@ -992,7 +1100,10 @@ function EditorSetDefaultPrefs() var domdoc; try { domdoc = window.editorShell.editorDocument; } catch (e) { dump( e + "\n"); } if ( !domdoc ) + { + dump("EditorSetDefaultPrefs: EDITOR DOCUMENT NOT FOUND\n"); return; + } // try to get preferences service var prefs = null; @@ -1011,7 +1122,7 @@ function EditorSetDefaultPrefs() // if not, create one and make it a child of the head tag // and set its content attribute to the value of the editor.author preference. - var prefAuthorString = prefs.CopyCharPref("editor.author"); + prefAuthorString = prefs.CopyCharPref("editor.author"); if ( prefAuthorString && prefAuthorString != 0) { var nodelist = domdoc.getElementsByTagName("meta"); @@ -1123,18 +1234,6 @@ function AddAttrToElem(dom, attr_name, attr_value, elem) } } - -function EditorDocumentLoaded() -{ - dump("The document was loaded in the content area\n"); - - //window.addEventListener("keyup", EditorReflectDocState, true, false); // post process, no capture - //window.addEventListener("dblclick", EditorDoDoubleClick, true, false); - - documentModified = (window.editorShell.documentStatus != 0); - return true; -} - function UpdateSaveButton(modified) { var saveButton = document.getElementById("saveButton"); @@ -1315,91 +1414,33 @@ function EditorInsertOrEditTable(insertAllowed) } } -function GetChildOffset(parent,child) -{ - if (!parent || !child) - return null; - - var nodeList = parent.childNodes; - var i; - if (nodeList) - { - for ( i = 0; i - + - + + - + - + @@ -340,13 +345,13 @@ - - + + - + @@ -465,7 +470,7 @@ position="10"> - + @@ -484,7 +489,7 @@ + position="13" hidden="true" disabled="true"> @@ -519,20 +524,20 @@ oncommand="EditorIndent('outdent')" position="17"/> - - - + + + + + @@ -568,9 +573,9 @@ - - - + + + @@ -625,11 +630,12 @@ - - + + - - + + + diff --git a/editor/ui/composer/locale/en-US/editor.dtd b/editor/ui/composer/locale/en-US/editor.dtd index ad5802fd0dbb..0275d52b0842 100644 --- a/editor/ui/composer/locale/en-US/editor.dtd +++ b/editor/ui/composer/locale/en-US/editor.dtd @@ -41,8 +41,9 @@ - - + + + diff --git a/editor/ui/composer/locale/en-US/editor.properties b/editor/ui/composer/locale/en-US/editor.properties index 4827600ae7c8..df9c5db52f4b 100644 --- a/editor/ui/composer/locale/en-US/editor.properties +++ b/editor/ui/composer/locale/en-US/editor.properties @@ -3,6 +3,8 @@ # # Note: embeded "\n" translate into breaks (
) # Don't tranalate!!! +# Don't translate strings like this: %variable% +# as they will be replaced using JavaScript # Yes=Yes No=No @@ -33,9 +35,9 @@ HTMLFiles=HTML Files IMGFiles=Image Files TextFiles=Text Files AllFiles=All Files -SaveFilePrompt=Save changes to BeforeClosing= before closing -QuestionMark=? +#Don't translate %title% and %reason% (this is the reason for asking user to close, such as "before closing") +SaveFilePrompt=Save changes to "%title%"%reason%? SaveFileFailed=Saving file failed! DocumentTitle=Document Title NeedDocTitle=Enter a title for the current page. The title identifies the page in the window title and bookmarks. @@ -45,11 +47,11 @@ EmptyHREFError=You must enter or choose a location (URL) to create a new link. LinkText=Link text: EnterLinkText=Enter text to display for the link: EmptyLinkTextError=You must enter some text for this link. -ValidateNumber1=The number you entered ( -ValidateNumber2=) is outside of allowed range. Please enter a number between -ValidateNumber3=and +#Don't translate: %n% %min% %max% +ValidateNumber=The number you entered (%n%) is outside of the allowed range. Please enter a number between %min% and %max% MissingAnchorNameError=You must enter a name for this anchor. -DuplicateAnchorNameError=already exists in this page. Please enter a different name. +#Don't translate %name% +DuplicateAnchorNameError="%name%" already exists in this page. Please enter a different name. BulletStyle=Bullet Style: SolidCircle=Solid circle OpenCircle=Open circle @@ -71,3 +73,12 @@ NoHeadings=(No headings without anchors) PageBackColor=Page Background Color CallBackColor=Cell Background Color TableBackColor=Table Background Color +Table=Table +TableCell=Table Cell +HLine=H.Line +Link=Link +Image=Image +NamedAnchor=Named Anchor +Tag=Tag +#Don't translate "%obj%" it will be replaced with one of above object nouns +ObjectProperties=%obj% Properties diff --git a/editor/ui/composer/locale/en-US/editorOverlay.dtd b/editor/ui/composer/locale/en-US/editorOverlay.dtd index af31ac37694b..be9d4c9ac50a 100644 --- a/editor/ui/composer/locale/en-US/editorOverlay.dtd +++ b/editor/ui/composer/locale/en-US/editorOverlay.dtd @@ -29,7 +29,7 @@ - + @@ -40,11 +40,14 @@ - + + + + @@ -74,7 +77,7 @@ - + @@ -93,12 +96,12 @@ - + - - + + - + @@ -232,9 +235,9 @@ - + - + @@ -423,21 +426,23 @@ - + - + - + - + - + + + - + @@ -447,6 +452,7 @@ + @@ -478,13 +484,8 @@ - - + - - - - @@ -493,7 +494,6 @@ - diff --git a/editor/ui/composer/skin/MANIFEST b/editor/ui/composer/skin/MANIFEST index 10a2431a629e..436e26b90659 100644 --- a/editor/ui/composer/skin/MANIFEST +++ b/editor/ui/composer/skin/MANIFEST @@ -57,6 +57,7 @@ images:savefile.gif images:savemod.gif images:publish.gif images:print.gif +images:preview.gif images:find.gif images:link-white.gif images:anchor-white.gif diff --git a/editor/ui/composer/skin/Makefile.in b/editor/ui/composer/skin/Makefile.in index 924883df0cf8..8db47dd3f52a 100644 --- a/editor/ui/composer/skin/Makefile.in +++ b/editor/ui/composer/skin/Makefile.in @@ -57,6 +57,7 @@ EXPORT_RESOURCE_TOOLBAR = \ $(srcdir)/images/savefile.gif \ $(srcdir)/images/savemod.gif \ $(srcdir)/images/publish.gif \ + $(srcdir)/images/preview.gif \ $(srcdir)/images/print.gif \ $(srcdir)/images/find.gif \ $(srcdir)/images/img-align-bottom.gif \ diff --git a/editor/ui/composer/skin/editor.css b/editor/ui/composer/skin/editor.css index be4fdbc9f6e9..ff526e0f5438 100644 --- a/editor/ui/composer/skin/editor.css +++ b/editor/ui/composer/skin/editor.css @@ -33,7 +33,7 @@ toolbox#EditorToolbox { min-width: 1px; } -toolbar#FormatToolbar { +toolbar#FormatToolbar, toolbar#TitleToolbar { border-bottom: 1px solid #003366; } @@ -105,6 +105,10 @@ toolbar#FormatToolbar titledbutton#underlineButton { text-decoration: underline; } +input#PageTitle, input#PageAuthor { + min-width: 5em; +} + div.toolbar-middle { /* trying to center vertically in the toolbar! */ vertical-align: middle; @@ -163,6 +167,11 @@ box#DisplayModeBar div.VerticalSeparator { margin: 1px 0px; } +/* SHOULD BE IN GLOBAL.CSS */ +menuitem[hidden="true"] { + visibility: collapse; +} + spring.spacer3 { width: 3px; height: 3px; @@ -173,6 +182,10 @@ spring.spacer5 { height: 5px; } +spring.spacer-throbber { + /* this is throbber box width - 12 */ + width: 56px; +} /* Image URLs for all Editor toolbar buttons */ titledbutton#newButton { @@ -192,6 +205,9 @@ titledbutton#saveButton[dirty="true"] { titledbutton#publishButton { list-style-image:url("chrome://editor/skin/images/publish.gif"); } +titledbutton#previewButton { + list-style-image:url("chrome://editor/skin/images/preview.gif"); +} titledbutton#printButton { list-style-image:url("chrome://editor/skin/images/print.gif"); } diff --git a/editor/ui/composer/skin/makefile.win b/editor/ui/composer/skin/makefile.win index 216744706f53..c04b9b2705ae 100644 --- a/editor/ui/composer/skin/makefile.win +++ b/editor/ui/composer/skin/makefile.win @@ -53,6 +53,7 @@ install:: $(DLL) $(MAKE_INSTALL) images\savefile.gif $(DIST)\bin\chrome\editor\skin\default\images $(MAKE_INSTALL) images\savemod.gif $(DIST)\bin\chrome\editor\skin\default\images $(MAKE_INSTALL) images\publish.gif $(DIST)\bin\chrome\editor\skin\default\images + $(MAKE_INSTALL) images\preview.gif $(DIST)\bin\chrome\editor\skin\default\images $(MAKE_INSTALL) images\print.gif $(DIST)\bin\chrome\editor\skin\default\images $(MAKE_INSTALL) images\find.gif $(DIST)\bin\chrome\editor\skin\default\images $(MAKE_INSTALL) images\img-align-bottom.gif $(DIST)\bin\chrome\editor\skin\default\images @@ -98,6 +99,7 @@ clobber:: rm -f $(DIST)\bin\chrome\editor\skin\default\images\savefile.gif rm -f $(DIST)\bin\chrome\editor\skin\default\images\savemod.gif rm -f $(DIST)\bin\chrome\editor\skin\default\images\publish.gif + rm -f $(DIST)\bin\chrome\editor\skin\default\images\preview.gif rm -f $(DIST)\bin\chrome\editor\skin\default\images\print.gif rm -f $(DIST)\bin\chrome\editor\skin\default\images\find.gif rm -f $(DIST)\bin\chrome\editor\skin\default\images\img-align-bottom.gif diff --git a/editor/ui/dialogs/content/EdDialogCommon.js b/editor/ui/dialogs/content/EdDialogCommon.js index a4732fce4f04..af863b83bbcf 100644 --- a/editor/ui/dialogs/content/EdDialogCommon.js +++ b/editor/ui/dialogs/content/EdDialogCommon.js @@ -131,7 +131,9 @@ function ValidateNumberString(value, minValue, maxValue) return number + ""; } } - message = editorShell.GetString("ValidateNumber1")+number+editorShell.GetString("ValidateNumber2")+" "+minValue+" "+editorShell.GetString("ValidateNumber3")+" "+maxValue; + message = editorShell.GetString("ValidateNumber"); + // Replace variable placeholders in message with number values + message = ((message.replace(/%n%/,number)).replace(/%min%/,minValue)).replace(/%max%/,maxValue); ShowInputErrorMessage(message); // Return an empty string to indicate error diff --git a/editor/ui/dialogs/content/EdNamedAnchorProps.js b/editor/ui/dialogs/content/EdNamedAnchorProps.js index 39547470cbdf..07d53000e5a3 100644 --- a/editor/ui/dialogs/content/EdNamedAnchorProps.js +++ b/editor/ui/dialogs/content/EdNamedAnchorProps.js @@ -110,7 +110,7 @@ function ValidateData() // Replace spaces with "_" else it causes trouble in URL parsing name = PrepareStringForURL(name); if (AnchorNameExists(name)) { - ShowInputErrorMessage("\""+name+"\" "+GetString("DuplicateAnchorNameError")); + ShowInputErrorMessage(GetString("DuplicateAnchorNameError").replace(/%name%/,name)); nameInput.focus(); return false; } diff --git a/editor/ui/dialogs/content/EdPageProps.xul b/editor/ui/dialogs/content/EdPageProps.xul index 4f45f9cb36cc..3ef0b067145d 100644 --- a/editor/ui/dialogs/content/EdPageProps.xul +++ b/editor/ui/dialogs/content/EdPageProps.xul @@ -27,6 +27,7 @@ +