diff --git a/mozilla/editor/base/nsComposerCommands.cpp b/mozilla/editor/base/nsComposerCommands.cpp index 93f683e3c3f..795164b1239 100644 --- a/mozilla/editor/base/nsComposerCommands.cpp +++ b/mozilla/editor/base/nsComposerCommands.cpp @@ -122,7 +122,10 @@ nsBaseStateUpdatingCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISuppo editorShell->GetEditor(getter_AddRefs(editor)); if (!editor) return NS_OK; - *outCmdEnabled = PR_TRUE; + // Enable commands only if not in HTML source edit mode + PRBool sourceMode = PR_FALSE; + editorShell->IsHTMLSourceMode(&sourceMode); + *outCmdEnabled = !sourceMode; // also udpate the command state return UpdateCommandState(aCommand, refCon); @@ -229,7 +232,8 @@ nsPrintingCommands::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) if (!editorShell) return NS_ERROR_NULL_POINTER; nsresult rv = NS_OK; - + editorShell->FinishHTMLSource(); + nsAutoString cmdString(aCommand); if (cmdString.EqualsWithConversion("cmd_print")) rv = editorShell->Print(); @@ -237,7 +241,7 @@ nsPrintingCommands::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) rv = NS_ERROR_NOT_IMPLEMENTED; else if (cmdString.EqualsWithConversion("cmd_printPreview")) rv = NS_ERROR_NOT_IMPLEMENTED; - + return rv; } @@ -270,10 +274,10 @@ nsSaveCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) if (editorShell) { + editorShell->FinishHTMLSource(); PRBool wasSaved; rv = editorShell->SaveDocument(PR_FALSE, PR_FALSE, &wasSaved); } - return rv; } @@ -298,10 +302,10 @@ nsSaveAsCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) nsresult rv = NS_OK; if (editorShell) { + editorShell->FinishHTMLSource(); PRBool wasSaved; rv = editorShell->SaveDocument(PR_TRUE, PR_FALSE, &wasSaved); } - return rv; } diff --git a/mozilla/editor/base/nsEditorShell.cpp b/mozilla/editor/base/nsEditorShell.cpp index f41114be2c6..89eb64297d6 100644 --- a/mozilla/editor/base/nsEditorShell.cpp +++ b/mozilla/editor/base/nsEditorShell.cpp @@ -249,6 +249,7 @@ GetTreeOwner(nsIDocShell* aDocShell, nsIBaseWindow** aBaseWindow) nsEditorShell::nsEditorShell() : mMailCompose(0) , mDisplayMode(eDisplayModeNormal) +, mHTMLSourceMode(PR_FALSE) , mWebShellWindow(nsnull) , mContentWindow(nsnull) , mParserObserver(nsnull) @@ -1260,10 +1261,15 @@ nsEditorShell::ApplyStyleSheet(const PRUnichar *url) NS_IMETHODIMP nsEditorShell::SetDisplayMode(PRInt32 aDisplayMode) { - // Ignore DisplayModeSource -- we don't do any style sheet changes - // The HTML Source display mode is handled in editor.js if (aDisplayMode == eDisplayModeSource) - return NS_OK; + { + // We track only display modes that involve style sheet changes + // with mDisplayMode, so use a separate bool for source mode + mHTMLSourceMode = PR_TRUE; + // The HTML Source display mode is handled in editor.js + return NS_OK; + } + mHTMLSourceMode = PR_FALSE; nsCOMPtr styleSheets = do_QueryInterface(mEditor); if (!styleSheets) return NS_NOINTERFACE; @@ -1357,6 +1363,36 @@ nsEditorShell::SetDisplayMode(PRInt32 aDisplayMode) return res; } +NS_IMETHODIMP +nsEditorShell::GetEditMode(PRInt32 *_retval) +{ + if (mHTMLSourceMode) + *_retval = eDisplayModeSource; + else + *_retval = mDisplayMode; + + return NS_OK; +} + +NS_IMETHODIMP +nsEditorShell::IsHTMLSourceMode(PRBool *_retval) +{ + *_retval = mHTMLSourceMode; + + return NS_OK; +} + +NS_IMETHODIMP +nsEditorShell::FinishHTMLSource(void) +{ + if (mHTMLSourceMode) + // Call the JS command to convert and switch to previous edit mode + return DoControllerCommand(NS_LITERAL_STRING("cmd_FinishHTMLSource")); + + return NS_OK; +} + + NS_IMETHODIMP nsEditorShell::DisplayParagraphMarks(PRBool aShowMarks) { @@ -1604,10 +1640,17 @@ nsEditorShell::CheckAndSaveDocument(const PRUnichar *reasonToSave, PRBool *_retv *_retval = PR_FALSE; } else if (result == eYes) { + FinishHTMLSource(); + // Either save to existing file or prompt for name (as for SaveAs) // We don't continue if we failed to save file (_retval is set to FALSE) rv = SaveDocument(PR_FALSE, PR_FALSE, _retval); } + else if (mHTMLSourceMode) // result == eNo + { + // User doesn't want to save document, so we just cancel source mode + rv = DoControllerCommand(NS_LITERAL_STRING("cmd_CancelHTMLSource")); + } } } } @@ -3775,7 +3818,17 @@ nsEditorShell::InsertTableCell(PRInt32 aNumber, PRBool bAfter) { nsCOMPtr tableEditor = do_QueryInterface(mEditor); if (tableEditor) + { + BeginBatchChanges(); result = tableEditor->InsertTableCell(aNumber, bAfter); + if (NS_SUCCEEDED(result)) + { + // Fix disturbances in table layout because of inserted cells + result = CheckPrefAndNormalizeTable(); + } + EndBatchChanges(); + return result; + } } break; @@ -3821,10 +3874,15 @@ nsEditorShell::DeleteTableCell(PRInt32 aNumber) nsCOMPtr tableEditor = do_QueryInterface(mEditor); if (tableEditor) { + BeginBatchChanges(); result = tableEditor->DeleteTableCell(aNumber); - // Don't return NS_EDITOR_ELEMENT_NOT_FOUND (passes NS_SUCCEEDED macro) - // to JavaScript - if(NS_SUCCEEDED(result)) return NS_OK; + if(NS_SUCCEEDED(result)) + { + // Fix disturbances in table layout because of deleted cells + result = CheckPrefAndNormalizeTable(); + } + EndBatchChanges(); + return result; } } break; @@ -4258,16 +4316,19 @@ nsEditorShell::GetCellDataAt(nsIDOMElement *tableElement, PRInt32 rowIndex, PRIn switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr tableEditor = do_QueryInterface(mEditor); - if (tableEditor) - result = tableEditor->GetCellDataAt(tableElement, rowIndex, colIndex, *_retval, - *aStartRowIndex, *aStartColIndex, - *aRowSpan, *aColSpan, - *aActualRowSpan, *aActualColSpan, - *aIsSelected); - } - break; + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetCellDataAt(tableElement, rowIndex, colIndex, *_retval, + *aStartRowIndex, *aStartColIndex, + *aRowSpan, *aColSpan, + *aActualRowSpan, *aActualColSpan, + *aIsSelected); + // Don't return NS_EDITOR_ELEMENT_NOT_FOUND (passes NS_SUCCEEDED macro) + // to JavaScript + if(NS_SUCCEEDED(result)) return NS_OK; + } + break; default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -5079,6 +5140,27 @@ nsEditorShell::DocumentIsRootDoc(nsIDocumentLoader* aLoader, PRBool& outIsRoot) return NS_OK; } +nsresult +nsEditorShell::CheckPrefAndNormalizeTable() +{ + nsresult res = NS_NOINTERFACE; + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + + if (htmlEditor) + { + NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &res); + if (NS_FAILED(res)) return NS_OK; + + PRBool normalizeTable = PR_FALSE; + if (NS_SUCCEEDED(prefs->GetBoolPref("editor.table.maintain_structure", &normalizeTable)) && normalizeTable) + return NormalizeTable(nsnull); + + return NS_OK; + } + return res; +} + + NS_IMETHODIMP nsEditorShell::HandleMouseClickOnElement(nsIDOMElement *aElement, PRInt32 aClickCount, PRInt32 x, PRInt32 y, PRBool *_retval) diff --git a/mozilla/editor/base/nsEditorShell.h b/mozilla/editor/base/nsEditorShell.h index c3fc45f9f5d..00278221a41 100644 --- a/mozilla/editor/base/nsEditorShell.h +++ b/mozilla/editor/base/nsEditorShell.h @@ -155,7 +155,12 @@ class nsEditorShell : public nsIEditorShell, nsresult DocumentContainsFrames(nsIDocumentLoader* aLoader, PRBool& outHasFrames); // is the document being loaded the root of a frameset, or a non-frameset doc? nsresult DocumentIsRootDoc(nsIDocumentLoader* aLoader, PRBool& outIsRoot); - + + // Check a preference and call NormalizeTable if pref is true + // Use after deleting or inserting table cells to automatically + // fix rowspan, colspan, and missing cells problems + nsresult CheckPrefAndNormalizeTable(); + nsCOMPtr mEditor; // this can be either an HTML or plain text (or other?) editor nsCOMPtr mSearchContext; // context used for search and replace. Owned by the appshell. nsCOMPtr mSpellChecker; @@ -188,6 +193,9 @@ class nsEditorShell : public nsIEditorShell, // Saves the current display mode to reload style sheets // after loading a url PRInt32 mDisplayMode; + // We don't store the HTMLSource mode in mDisplayMode, + // so we need to track it separately + PRBool mHTMLSourceMode; nsIDOMWindow *mWebShellWindow; // weak reference //nsIDOMWindow *mContentWindow; // weak reference diff --git a/mozilla/editor/base/nsHTMLEditor.h b/mozilla/editor/base/nsHTMLEditor.h index 9f9ee801910..d387a15348f 100644 --- a/mozilla/editor/base/nsHTMLEditor.h +++ b/mozilla/editor/base/nsHTMLEditor.h @@ -404,7 +404,7 @@ protected: PRBool AllCellsInRowSelected(nsIDOMElement *aTable, PRInt32 aRowIndex, PRInt32 aNumberOfColumns); PRBool AllCellsInColumnSelected(nsIDOMElement *aTable, PRInt32 aColIndex, PRInt32 aNumberOfRows); - PRBool IsEmptyCell(nsIDOMElement *aCell); + PRBool IsEmptyCell(nsIDOMElement *aCell); // Most insert methods need to get the same basic context data // Any of the pointers may be null if you don't need that datum (for more efficiency) diff --git a/mozilla/editor/base/nsTableEditor.cpp b/mozilla/editor/base/nsTableEditor.cpp index c50468a5c83..59cdd6ea730 100644 --- a/mozilla/editor/base/nsTableEditor.cpp +++ b/mozilla/editor/base/nsTableEditor.cpp @@ -445,8 +445,9 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) } } else { // We are inserting after all existing columns - //TODO: Make sure table is "well formed" (call NormalizeTable) + // Make sure table is "well formed" // before appending new column + NormalizeTable(table); // Get current row and append new cells after last cell in row if(rowIndex == 0) @@ -468,7 +469,7 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) // 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 + // We must use NormalizeTable first to assure // that there are cells in each cellmap location selection->Collapse(curCell, 0); res = InsertTableCell(aNumber, PR_TRUE); @@ -844,9 +845,7 @@ nsHTMLEditor::DeleteTableCell(PRInt32 aNumber) { // 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 SetSelectionAfterTableEdit in it's destructor - selection->ClearSelection(); nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, PR_FALSE); nsAutoTxnsConserveSelection dontChangeSelection(this); @@ -1161,8 +1160,11 @@ nsHTMLEditor::DeleteTableRow(PRInt32 aNumber) res = GetCellIndexes(firstCell, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; } + //We control selection resetting after the insert... nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, PR_FALSE); + // Don't change selection during deletions + nsAutoTxnsConserveSelection dontChangeSelection(this); if (firstCell && rangeCount > 1) { @@ -1198,10 +1200,6 @@ nsHTMLEditor::DeleteTableRow(PRInt32 aNumber) // 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 SetSelectionAfterTableEdit)! - selection->ClearSelection(); - for (PRInt32 i = 0; i < aNumber; i++) { res = DeleteRow(table, startRowIndex); @@ -1227,42 +1225,66 @@ nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, PRInt32 aRowIndex) nsCOMPtr cell; nsCOMPtr cellInDeleteRow; - PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; PRBool isSelected; PRInt32 colIndex = 0; nsresult res = NS_OK; + // Prevent rules testing until we're done + nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext); + + // The list of cells we will change rowspan in + // and the new rowspan values for each + nsVoidArray spanCellList; + nsVoidArray newSpanList; + // Scan through cells in row to do rowspan adjustments // Note that after we delete row, startRowIndex will point to the // cells in the next row to be deleted do { - nsCOMPtr cell; res = GetCellDataAt(aTable, aRowIndex, colIndex, *getter_AddRefs(cell), - curStartRowIndex, curStartColIndex, rowSpan, colSpan, + startRowIndex, startColIndex, 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 + // Compensate for cells that don't start or extend below the row we are deleting if (cell) { - //Real colspan must always be >= 1 + // Real colspan must always be >= 1 NS_ASSERTION((actualColSpan > 0),"Effective COLSPAN = 0 in DeleteTableRow"); - if (curStartRowIndex < aRowIndex) + if (startRowIndex < aRowIndex) { - // We have a cell spanning this location + // Cell starts in row above us // 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(cell, PR_MAX((aRowIndex - curStartRowIndex), actualRowSpan-1)); + { + // Build list of cells to change rowspan + // We can't do it now since it upsets cell map, + // so we will do it after deleting the row + spanCellList.AppendElement((void*)cell.get()); + newSpanList.AppendElement((void*)PR_MAX((aRowIndex - startRowIndex), actualRowSpan-1)); + } } - else if (!cellInDeleteRow) + else { - cellInDeleteRow = cell; + if (rowSpan > 1) + { + //Cell spans below row to delete, + // so we must insert new cells to keep rows below even + // Note that we test "rowSpan" so we don't do this if rowSpan = 0 (automatic readjustment) + res = SplitCellIntoRows(aTable, startRowIndex, startColIndex, + aRowIndex - startRowIndex + 1, // The row above the row to insert new cell into + actualRowSpan - 1, nsnull); // Span remaining below + if (NS_FAILED(res)) return res; + } + if (!cellInDeleteRow) + cellInDeleteRow = cell; // Reference cell to find row to delete } - // Skip over locations spanned by this cell + // Skip over other columns spanned by this cell colIndex += actualColSpan; } } while (cell); @@ -1271,12 +1293,7 @@ nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, PRInt32 aRowIndex) if (!cellInDeleteRow) return NS_ERROR_FAILURE; - //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 + // Delete the entire row nsCOMPtr parentRow; res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow, getter_AddRefs(parentRow)); if (NS_FAILED(res)) return res; @@ -1286,6 +1303,25 @@ nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, PRInt32 aRowIndex) res = DeleteNode(parentRow); if (NS_FAILED(res)) return res; } + + // Now we can set new rowspans for cells stored above + nsIDOMElement *cellPtr; + PRInt32 newSpan; + PRInt32 count; + while ((count = spanCellList.Count())) + { + // go backwards to keep nsVoidArray from mem-moving everything each time + count--; // nsVoidArray is zero based + cellPtr = (nsIDOMElement*)spanCellList.ElementAt(count); + spanCellList.RemoveElementAt(count); + newSpan = (PRInt32)newSpanList.ElementAt(count); + newSpanList.RemoveElementAt(count); + if (cellPtr) + { + res = SetRowSpan(cellPtr, newSpan); + if (NS_FAILED(res)) return res; + } + } return NS_OK; } @@ -3289,4 +3325,3 @@ nsHTMLEditor::IsEmptyCell(nsIDOMElement *aCell) } return PR_FALSE; } - diff --git a/mozilla/editor/composer/src/nsComposerCommands.cpp b/mozilla/editor/composer/src/nsComposerCommands.cpp index 93f683e3c3f..795164b1239 100644 --- a/mozilla/editor/composer/src/nsComposerCommands.cpp +++ b/mozilla/editor/composer/src/nsComposerCommands.cpp @@ -122,7 +122,10 @@ nsBaseStateUpdatingCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISuppo editorShell->GetEditor(getter_AddRefs(editor)); if (!editor) return NS_OK; - *outCmdEnabled = PR_TRUE; + // Enable commands only if not in HTML source edit mode + PRBool sourceMode = PR_FALSE; + editorShell->IsHTMLSourceMode(&sourceMode); + *outCmdEnabled = !sourceMode; // also udpate the command state return UpdateCommandState(aCommand, refCon); @@ -229,7 +232,8 @@ nsPrintingCommands::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) if (!editorShell) return NS_ERROR_NULL_POINTER; nsresult rv = NS_OK; - + editorShell->FinishHTMLSource(); + nsAutoString cmdString(aCommand); if (cmdString.EqualsWithConversion("cmd_print")) rv = editorShell->Print(); @@ -237,7 +241,7 @@ nsPrintingCommands::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) rv = NS_ERROR_NOT_IMPLEMENTED; else if (cmdString.EqualsWithConversion("cmd_printPreview")) rv = NS_ERROR_NOT_IMPLEMENTED; - + return rv; } @@ -270,10 +274,10 @@ nsSaveCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) if (editorShell) { + editorShell->FinishHTMLSource(); PRBool wasSaved; rv = editorShell->SaveDocument(PR_FALSE, PR_FALSE, &wasSaved); } - return rv; } @@ -298,10 +302,10 @@ nsSaveAsCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) nsresult rv = NS_OK; if (editorShell) { + editorShell->FinishHTMLSource(); PRBool wasSaved; rv = editorShell->SaveDocument(PR_TRUE, PR_FALSE, &wasSaved); } - return rv; } diff --git a/mozilla/editor/composer/src/nsEditorShell.cpp b/mozilla/editor/composer/src/nsEditorShell.cpp index f41114be2c6..89eb64297d6 100644 --- a/mozilla/editor/composer/src/nsEditorShell.cpp +++ b/mozilla/editor/composer/src/nsEditorShell.cpp @@ -249,6 +249,7 @@ GetTreeOwner(nsIDocShell* aDocShell, nsIBaseWindow** aBaseWindow) nsEditorShell::nsEditorShell() : mMailCompose(0) , mDisplayMode(eDisplayModeNormal) +, mHTMLSourceMode(PR_FALSE) , mWebShellWindow(nsnull) , mContentWindow(nsnull) , mParserObserver(nsnull) @@ -1260,10 +1261,15 @@ nsEditorShell::ApplyStyleSheet(const PRUnichar *url) NS_IMETHODIMP nsEditorShell::SetDisplayMode(PRInt32 aDisplayMode) { - // Ignore DisplayModeSource -- we don't do any style sheet changes - // The HTML Source display mode is handled in editor.js if (aDisplayMode == eDisplayModeSource) - return NS_OK; + { + // We track only display modes that involve style sheet changes + // with mDisplayMode, so use a separate bool for source mode + mHTMLSourceMode = PR_TRUE; + // The HTML Source display mode is handled in editor.js + return NS_OK; + } + mHTMLSourceMode = PR_FALSE; nsCOMPtr styleSheets = do_QueryInterface(mEditor); if (!styleSheets) return NS_NOINTERFACE; @@ -1357,6 +1363,36 @@ nsEditorShell::SetDisplayMode(PRInt32 aDisplayMode) return res; } +NS_IMETHODIMP +nsEditorShell::GetEditMode(PRInt32 *_retval) +{ + if (mHTMLSourceMode) + *_retval = eDisplayModeSource; + else + *_retval = mDisplayMode; + + return NS_OK; +} + +NS_IMETHODIMP +nsEditorShell::IsHTMLSourceMode(PRBool *_retval) +{ + *_retval = mHTMLSourceMode; + + return NS_OK; +} + +NS_IMETHODIMP +nsEditorShell::FinishHTMLSource(void) +{ + if (mHTMLSourceMode) + // Call the JS command to convert and switch to previous edit mode + return DoControllerCommand(NS_LITERAL_STRING("cmd_FinishHTMLSource")); + + return NS_OK; +} + + NS_IMETHODIMP nsEditorShell::DisplayParagraphMarks(PRBool aShowMarks) { @@ -1604,10 +1640,17 @@ nsEditorShell::CheckAndSaveDocument(const PRUnichar *reasonToSave, PRBool *_retv *_retval = PR_FALSE; } else if (result == eYes) { + FinishHTMLSource(); + // Either save to existing file or prompt for name (as for SaveAs) // We don't continue if we failed to save file (_retval is set to FALSE) rv = SaveDocument(PR_FALSE, PR_FALSE, _retval); } + else if (mHTMLSourceMode) // result == eNo + { + // User doesn't want to save document, so we just cancel source mode + rv = DoControllerCommand(NS_LITERAL_STRING("cmd_CancelHTMLSource")); + } } } } @@ -3775,7 +3818,17 @@ nsEditorShell::InsertTableCell(PRInt32 aNumber, PRBool bAfter) { nsCOMPtr tableEditor = do_QueryInterface(mEditor); if (tableEditor) + { + BeginBatchChanges(); result = tableEditor->InsertTableCell(aNumber, bAfter); + if (NS_SUCCEEDED(result)) + { + // Fix disturbances in table layout because of inserted cells + result = CheckPrefAndNormalizeTable(); + } + EndBatchChanges(); + return result; + } } break; @@ -3821,10 +3874,15 @@ nsEditorShell::DeleteTableCell(PRInt32 aNumber) nsCOMPtr tableEditor = do_QueryInterface(mEditor); if (tableEditor) { + BeginBatchChanges(); result = tableEditor->DeleteTableCell(aNumber); - // Don't return NS_EDITOR_ELEMENT_NOT_FOUND (passes NS_SUCCEEDED macro) - // to JavaScript - if(NS_SUCCEEDED(result)) return NS_OK; + if(NS_SUCCEEDED(result)) + { + // Fix disturbances in table layout because of deleted cells + result = CheckPrefAndNormalizeTable(); + } + EndBatchChanges(); + return result; } } break; @@ -4258,16 +4316,19 @@ nsEditorShell::GetCellDataAt(nsIDOMElement *tableElement, PRInt32 rowIndex, PRIn switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr tableEditor = do_QueryInterface(mEditor); - if (tableEditor) - result = tableEditor->GetCellDataAt(tableElement, rowIndex, colIndex, *_retval, - *aStartRowIndex, *aStartColIndex, - *aRowSpan, *aColSpan, - *aActualRowSpan, *aActualColSpan, - *aIsSelected); - } - break; + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetCellDataAt(tableElement, rowIndex, colIndex, *_retval, + *aStartRowIndex, *aStartColIndex, + *aRowSpan, *aColSpan, + *aActualRowSpan, *aActualColSpan, + *aIsSelected); + // Don't return NS_EDITOR_ELEMENT_NOT_FOUND (passes NS_SUCCEEDED macro) + // to JavaScript + if(NS_SUCCEEDED(result)) return NS_OK; + } + break; default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -5079,6 +5140,27 @@ nsEditorShell::DocumentIsRootDoc(nsIDocumentLoader* aLoader, PRBool& outIsRoot) return NS_OK; } +nsresult +nsEditorShell::CheckPrefAndNormalizeTable() +{ + nsresult res = NS_NOINTERFACE; + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + + if (htmlEditor) + { + NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &res); + if (NS_FAILED(res)) return NS_OK; + + PRBool normalizeTable = PR_FALSE; + if (NS_SUCCEEDED(prefs->GetBoolPref("editor.table.maintain_structure", &normalizeTable)) && normalizeTable) + return NormalizeTable(nsnull); + + return NS_OK; + } + return res; +} + + NS_IMETHODIMP nsEditorShell::HandleMouseClickOnElement(nsIDOMElement *aElement, PRInt32 aClickCount, PRInt32 x, PRInt32 y, PRBool *_retval) diff --git a/mozilla/editor/composer/src/nsEditorShell.h b/mozilla/editor/composer/src/nsEditorShell.h index c3fc45f9f5d..00278221a41 100644 --- a/mozilla/editor/composer/src/nsEditorShell.h +++ b/mozilla/editor/composer/src/nsEditorShell.h @@ -155,7 +155,12 @@ class nsEditorShell : public nsIEditorShell, nsresult DocumentContainsFrames(nsIDocumentLoader* aLoader, PRBool& outHasFrames); // is the document being loaded the root of a frameset, or a non-frameset doc? nsresult DocumentIsRootDoc(nsIDocumentLoader* aLoader, PRBool& outIsRoot); - + + // Check a preference and call NormalizeTable if pref is true + // Use after deleting or inserting table cells to automatically + // fix rowspan, colspan, and missing cells problems + nsresult CheckPrefAndNormalizeTable(); + nsCOMPtr mEditor; // this can be either an HTML or plain text (or other?) editor nsCOMPtr mSearchContext; // context used for search and replace. Owned by the appshell. nsCOMPtr mSpellChecker; @@ -188,6 +193,9 @@ class nsEditorShell : public nsIEditorShell, // Saves the current display mode to reload style sheets // after loading a url PRInt32 mDisplayMode; + // We don't store the HTMLSource mode in mDisplayMode, + // so we need to track it separately + PRBool mHTMLSourceMode; nsIDOMWindow *mWebShellWindow; // weak reference //nsIDOMWindow *mContentWindow; // weak reference diff --git a/mozilla/editor/idl/nsIEditorShell.idl b/mozilla/editor/idl/nsIEditorShell.idl index fc17a971945..03a08479667 100644 --- a/mozilla/editor/idl/nsIEditorShell.idl +++ b/mozilla/editor/idl/nsIEditorShell.idl @@ -552,6 +552,25 @@ interface nsIEditorShell : nsISupports */ void SetDisplayMode(in PRInt32 displayMode); + /** Get current mode for editing + * Returns: + * 0 (eDisplayModePreview,) + * 1 (eDisplayModeNormal) + * 2 (eDisplayModeAllTags) + * 3 (eDisplayModeSource) + */ + PRInt32 GetEditMode(); + + /** For quicker test if we are in HTML Source mode + */ + boolean IsHTMLSourceMode(); + + /** Save the current HTML source edit session + * by inserting it into the document, + * replacing existing DOM + */ + void FinishHTMLSource(); + /** Add/remove an overridestyle sheet to show paragraph marks * */ diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index 9f9ee801910..d387a15348f 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -404,7 +404,7 @@ protected: PRBool AllCellsInRowSelected(nsIDOMElement *aTable, PRInt32 aRowIndex, PRInt32 aNumberOfColumns); PRBool AllCellsInColumnSelected(nsIDOMElement *aTable, PRInt32 aColIndex, PRInt32 aNumberOfRows); - PRBool IsEmptyCell(nsIDOMElement *aCell); + PRBool IsEmptyCell(nsIDOMElement *aCell); // Most insert methods need to get the same basic context data // Any of the pointers may be null if you don't need that datum (for more efficiency) diff --git a/mozilla/editor/libeditor/html/nsTableEditor.cpp b/mozilla/editor/libeditor/html/nsTableEditor.cpp index c50468a5c83..59cdd6ea730 100644 --- a/mozilla/editor/libeditor/html/nsTableEditor.cpp +++ b/mozilla/editor/libeditor/html/nsTableEditor.cpp @@ -445,8 +445,9 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) } } else { // We are inserting after all existing columns - //TODO: Make sure table is "well formed" (call NormalizeTable) + // Make sure table is "well formed" // before appending new column + NormalizeTable(table); // Get current row and append new cells after last cell in row if(rowIndex == 0) @@ -468,7 +469,7 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) // 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 + // We must use NormalizeTable first to assure // that there are cells in each cellmap location selection->Collapse(curCell, 0); res = InsertTableCell(aNumber, PR_TRUE); @@ -844,9 +845,7 @@ nsHTMLEditor::DeleteTableCell(PRInt32 aNumber) { // 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 SetSelectionAfterTableEdit in it's destructor - selection->ClearSelection(); nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, PR_FALSE); nsAutoTxnsConserveSelection dontChangeSelection(this); @@ -1161,8 +1160,11 @@ nsHTMLEditor::DeleteTableRow(PRInt32 aNumber) res = GetCellIndexes(firstCell, startRowIndex, startColIndex); if (NS_FAILED(res)) return res; } + //We control selection resetting after the insert... nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, PR_FALSE); + // Don't change selection during deletions + nsAutoTxnsConserveSelection dontChangeSelection(this); if (firstCell && rangeCount > 1) { @@ -1198,10 +1200,6 @@ nsHTMLEditor::DeleteTableRow(PRInt32 aNumber) // 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 SetSelectionAfterTableEdit)! - selection->ClearSelection(); - for (PRInt32 i = 0; i < aNumber; i++) { res = DeleteRow(table, startRowIndex); @@ -1227,42 +1225,66 @@ nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, PRInt32 aRowIndex) nsCOMPtr cell; nsCOMPtr cellInDeleteRow; - PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; PRBool isSelected; PRInt32 colIndex = 0; nsresult res = NS_OK; + // Prevent rules testing until we're done + nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext); + + // The list of cells we will change rowspan in + // and the new rowspan values for each + nsVoidArray spanCellList; + nsVoidArray newSpanList; + // Scan through cells in row to do rowspan adjustments // Note that after we delete row, startRowIndex will point to the // cells in the next row to be deleted do { - nsCOMPtr cell; res = GetCellDataAt(aTable, aRowIndex, colIndex, *getter_AddRefs(cell), - curStartRowIndex, curStartColIndex, rowSpan, colSpan, + startRowIndex, startColIndex, 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 + // Compensate for cells that don't start or extend below the row we are deleting if (cell) { - //Real colspan must always be >= 1 + // Real colspan must always be >= 1 NS_ASSERTION((actualColSpan > 0),"Effective COLSPAN = 0 in DeleteTableRow"); - if (curStartRowIndex < aRowIndex) + if (startRowIndex < aRowIndex) { - // We have a cell spanning this location + // Cell starts in row above us // 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(cell, PR_MAX((aRowIndex - curStartRowIndex), actualRowSpan-1)); + { + // Build list of cells to change rowspan + // We can't do it now since it upsets cell map, + // so we will do it after deleting the row + spanCellList.AppendElement((void*)cell.get()); + newSpanList.AppendElement((void*)PR_MAX((aRowIndex - startRowIndex), actualRowSpan-1)); + } } - else if (!cellInDeleteRow) + else { - cellInDeleteRow = cell; + if (rowSpan > 1) + { + //Cell spans below row to delete, + // so we must insert new cells to keep rows below even + // Note that we test "rowSpan" so we don't do this if rowSpan = 0 (automatic readjustment) + res = SplitCellIntoRows(aTable, startRowIndex, startColIndex, + aRowIndex - startRowIndex + 1, // The row above the row to insert new cell into + actualRowSpan - 1, nsnull); // Span remaining below + if (NS_FAILED(res)) return res; + } + if (!cellInDeleteRow) + cellInDeleteRow = cell; // Reference cell to find row to delete } - // Skip over locations spanned by this cell + // Skip over other columns spanned by this cell colIndex += actualColSpan; } } while (cell); @@ -1271,12 +1293,7 @@ nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, PRInt32 aRowIndex) if (!cellInDeleteRow) return NS_ERROR_FAILURE; - //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 + // Delete the entire row nsCOMPtr parentRow; res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow, getter_AddRefs(parentRow)); if (NS_FAILED(res)) return res; @@ -1286,6 +1303,25 @@ nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, PRInt32 aRowIndex) res = DeleteNode(parentRow); if (NS_FAILED(res)) return res; } + + // Now we can set new rowspans for cells stored above + nsIDOMElement *cellPtr; + PRInt32 newSpan; + PRInt32 count; + while ((count = spanCellList.Count())) + { + // go backwards to keep nsVoidArray from mem-moving everything each time + count--; // nsVoidArray is zero based + cellPtr = (nsIDOMElement*)spanCellList.ElementAt(count); + spanCellList.RemoveElementAt(count); + newSpan = (PRInt32)newSpanList.ElementAt(count); + newSpanList.RemoveElementAt(count); + if (cellPtr) + { + res = SetRowSpan(cellPtr, newSpan); + if (NS_FAILED(res)) return res; + } + } return NS_OK; } @@ -3289,4 +3325,3 @@ nsHTMLEditor::IsEmptyCell(nsIDOMElement *aCell) } return PR_FALSE; } - diff --git a/mozilla/editor/ui/composer/content/ComposerCommands.js b/mozilla/editor/ui/composer/content/ComposerCommands.js index 2842d2a73fd..f03b1a419e6 100644 --- a/mozilla/editor/ui/composer/content/ComposerCommands.js +++ b/mozilla/editor/ui/composer/content/ComposerCommands.js @@ -90,6 +90,8 @@ function SetupControllerCommands() gComposerCommandManager.registerCommand("cmd_tableJoinCells", nsJoinTableCellsCommand); gComposerCommandManager.registerCommand("cmd_tableSplitCell", nsSplitTableCellCommand); gComposerCommandManager.registerCommand("cmd_NormalizeTable", nsNormalizeTableCommand); + gComposerCommandManager.registerCommand("cmd_FinishHTMLSource", nsFinishHTMLSource); + gComposerCommandManager.registerCommand("cmd_CancelHTMLSource", nsCancelHTMLSource); } //----------------------------------------------------------------------------------- @@ -169,6 +171,8 @@ var nsOpenCommand = doCommand: function(aCommand) { +dump("*** nsOpenCommand: doCommand\n"); + var fp = Components.classes["component://mozilla/filepicker"].createInstance(nsIFilePicker); fp.init(window, window.editorShell.GetString("OpenHTMLFile"), nsIFilePicker.modeOpen); @@ -207,10 +211,12 @@ var nsSaveAsCharsetCommand = doCommand: function(aCommand) { window.ok = false; - if(window.openDialog("chrome://editor/content/EditorSaveAsCharset.xul","_blank", "chrome,close,titlebar,modal")) + // In editor.js + FinishHTMLSource(); + if (window.openDialog("chrome://editor/content/EditorSaveAsCharset.xul","_blank", "chrome,close,titlebar,modal")) { - if( window.ok ) - return window.editorShell.saveDocument(true, false); + if( window.ok ) + return window.editorShell.saveDocument(true, false); } return false; } @@ -227,48 +233,43 @@ var nsRevertCommand = doCommand: function(aCommand) { - if (window.editorShell && - window.editorShell.documentModified && - window.editorShell.editorDocument.location != "about:blank") + // Confirm with the user to abandon current changes + if (commonDialogsService) { - // Confirm with the user to abandon current changes - if (commonDialogsService) - { - var result = {value:0}; + var result = {value:0}; - // Put the page title in the message string - var title = window.editorShell.editorDocument.title; - if (!title || title.length == 0) - title = window.editorShell.GetTitle("untitled"); + // Put the page title in the message string + var title = window.editorShell.editorDocument.title; + if (!title || title.length == 0) + title = window.editorShell.GetTitle("untitled"); - var msg = window.editorShell.GetString("AbandonChanges").replace(/%title%/,title); + var msg = window.editorShell.GetString("AbandonChanges").replace(/%title%/,title); - commonDialogsService.UniversalDialog( - window, - null, - window.editorShell.GetString("RevertCaption"), - msg, - null, - window.editorShell.GetString("Revert"), - window.editorShell.GetString("Cancel"), - null, - null, - null, - null, - {value:0}, - {value:0}, - "chrome://global/skin/question-icon.gif", - {value:"false"}, - 2, - 0, - 0, - result - ); + commonDialogsService.UniversalDialog( + window, + null, + window.editorShell.GetString("RevertCaption"), + msg, + null, + window.editorShell.GetString("Revert"), + window.editorShell.GetString("Cancel"), + null, + null, + null, + null, + {value:0}, + {value:0}, + "chrome://global/skin/question-icon.gif", + {value:"false"}, + 2, + 0, + 0, + result + ); - // Reload page if first button (Rever) was pressed - if(result.value == 0) - window.editorShell.LoadUrl(editorShell.editorDocument.location); - } + // Reload page if first button (Rever) was pressed + if(result.value == 0) + window.editorShell.LoadUrl(editorShell.editorDocument.location); } } }; @@ -317,6 +318,7 @@ var nsPreviewCommand = doCommand: function(aCommand) { + FinishHTMLSource(); if (!editorShell.CheckAndSaveDocument(window.editorShell.GetString("BeforePreview"))) return; @@ -346,6 +348,8 @@ var nsQuitCommand = doCommand: function(aCommand) { + // In editor.js + FinishHTMLSource(); goQuitApplication(); } }; @@ -393,7 +397,6 @@ var nsSpellingCommand = var spellChecker = window.editorShell.QueryInterface(Components.interfaces.nsIEditorSpellCheck); if (spellChecker) { - // dump("Check Spelling starting...\n"); // Start the spell checker module. Return is first misspelled word try { spellChecker.InitSpellChecker(); @@ -462,16 +465,16 @@ var nsHLineCommand = // Inserting an HLine is different in that we don't use properties dialog // unless we are editing an existing line's attributes // We get the last-used attributes from the prefs and insert immediately - + tagName = "hr"; hLine = window.editorShell.GetSelectedElement(tagName); - + if (hLine) { // We only open the dialog for an existing HRule window.openDialog("chrome://editor/content/EdHLineProps.xul", "_blank", "chrome,close,titlebar,modal"); } else { hLine = window.editorShell.CreateElementWithDefaults(tagName); - + if (hLine) { // We change the default attributes to those saved in the user prefs @@ -480,7 +483,7 @@ var nsHLineCommand = var height; var shading; var ud = "undefined"; - + try { var align = gPrefs.GetIntPref("editor.hrule.align"); if (align == 0 ) { @@ -496,12 +499,12 @@ var nsHLineCommand = var percent = gPrefs.GetBoolPref("editor.hrule.width_percent"); if (percent) width = width +"%"; - + hLine.setAttribute("width", width); - + var height = gPrefs.GetIntPref("editor.hrule.height"); hLine.setAttribute("size", String(height)); - + var shading = gPrefs.GetBoolPref("editor.hrule.shading"); if (shading) { hLine.removeAttribute("noshade"); @@ -750,8 +753,7 @@ var nsInsertOrEditTableCommand = doCommand: function(aCommand) { dump("nsInsertOrEditTableCommand\n"); - if (this.isCommandEnabled(aCommand)) - EditorInsertOrEditTable(true); + EditorInsertOrEditTable(true); } }; @@ -765,8 +767,7 @@ var nsEditTableCommand = doCommand: function(aCommand) { dump("nsEditTableCommand\n"); - if (this.isCommandEnabled(aCommand)) - EditorInsertOrEditTable(false); + EditorInsertOrEditTable(false); } }; @@ -780,11 +781,8 @@ var nsSelectTableCommand = doCommand: function(aCommand) { dump("nsSelectTableCommand\n"); - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.SelectTable(); - window._content.focus(); - } + window.editorShell.SelectTable(); + window._content.focus(); } }; @@ -797,11 +795,8 @@ var nsSelectTableRowCommand = doCommand: function(aCommand) { dump("nsSelectTableRowCommand\n"); - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.SelectTableRow(); - window._content.focus(); - } + window.editorShell.SelectTableRow(); + window._content.focus(); } }; @@ -814,11 +809,8 @@ var nsSelectTableColumnCommand = doCommand: function(aCommand) { dump("nsSelectTableColumnCommand\n"); - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.SelectTableColumn(); - window._content.focus(); - } + window.editorShell.SelectTableColumn(); + window._content.focus(); } }; @@ -831,11 +823,8 @@ var nsSelectTableCellCommand = doCommand: function(aCommand) { dump("nsSelectTableCellCommand\n"); - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.SelectTableCell(); - window._content.focus(); - } + window.editorShell.SelectTableCell(); + window._content.focus(); } }; @@ -848,11 +837,8 @@ var nsSelectAllTableCellsCommand = doCommand: function(aCommand) { dump("nsSelectAllTableCellsCommand\n"); - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.SelectAllTableCells(); - window._content.focus(); - } + window.editorShell.SelectAllTableCells(); + window._content.focus(); } }; @@ -865,8 +851,7 @@ var nsInsertTableCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - EditorInsertTable(); + EditorInsertTable(); } }; @@ -878,11 +863,8 @@ var nsInsertTableRowAboveCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.InsertTableRow(1, false); - window._content.focus(); - } + window.editorShell.InsertTableRow(1, false); + window._content.focus(); } }; @@ -894,11 +876,8 @@ var nsInsertTableRowBelowCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.InsertTableRow(1,true); - window._content.focus(); - } + window.editorShell.InsertTableRow(1,true); + window._content.focus(); } }; @@ -910,11 +889,8 @@ var nsInsertTableColumnBeforeCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.InsertTableColumn(1, false); - window._content.focus(); - } + window.editorShell.InsertTableColumn(1, false); + window._content.focus(); } }; @@ -926,11 +902,8 @@ var nsInsertTableColumnAfterCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.InsertTableColumn(1, true); - window._content.focus(); - } + window.editorShell.InsertTableColumn(1, true); + window._content.focus(); } }; @@ -942,8 +915,7 @@ var nsInsertTableCellBeforeCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - window.editorShell.InsertTableCell(1, false); + window.editorShell.InsertTableCell(1, false); } }; @@ -955,11 +927,8 @@ var nsInsertTableCellAfterCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.InsertTableCell(1, true); - window._content.focus(); - } + window.editorShell.InsertTableCell(1, true); + window._content.focus(); } }; @@ -972,11 +941,8 @@ var nsDeleteTableCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.DeleteTable(); - window._content.focus(); - } + window.editorShell.DeleteTable(); + window._content.focus(); } }; @@ -988,11 +954,9 @@ var nsDeleteTableRowCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.DeleteTableRow(1); - window._content.focus(); - } +dump("nsDeleteTableRowCommand: doCommand\n"); + window.editorShell.DeleteTableRow(1); + window._content.focus(); } }; @@ -1004,11 +968,8 @@ var nsDeleteTableColumnCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.DeleteTableColumn(1); - window._content.focus(); - } + window.editorShell.DeleteTableColumn(1); + window._content.focus(); } }; @@ -1020,11 +981,8 @@ var nsDeleteTableCellCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.DeleteTableCell(1); - window._content.focus(); - } + window.editorShell.DeleteTableCell(1); + window._content.focus(); } }; @@ -1036,11 +994,8 @@ var nsDeleteTableCellContentsCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.DeleteTableCellContents(); - window._content.focus(); - } + window.editorShell.DeleteTableCellContents(); + window._content.focus(); } }; @@ -1054,12 +1009,9 @@ var nsNormalizeTableCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - // Use nsnull to let editor find table enclosing current selection - window.editorShell.NormalizeTable(null); - window._content.focus(); - } + // Use nsnull to let editor find table enclosing current selection + window.editorShell.NormalizeTable(null); + window._content.focus(); } }; @@ -1098,12 +1050,9 @@ var nsJoinTableCellsCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - // Param: Don't merge non-contiguous cells - window.editorShell.JoinTableCells(false); - window._content.focus(); - } + // Param: Don't merge non-contiguous cells + window.editorShell.JoinTableCells(false); + window._content.focus(); } }; @@ -1135,11 +1084,8 @@ var nsSplitTableCellCommand = }, doCommand: function(aCommand) { - if (this.isCommandEnabled(aCommand)) - { - window.editorShell.SplitTableCell(); - window._content.focus(); - } + window.editorShell.SplitTableCell(); + window._content.focus(); } }; @@ -1157,3 +1103,28 @@ var nsPreferencesCommand = }; +var nsFinishHTMLSource = +{ + isCommandEnabled: function(aCommand, dummy) + { + return true; + }, + doCommand: function(aCommand) + { + // In editor.js + FinishHTMLSource(); + } +}; + +var nsCancelHTMLSource = +{ + isCommandEnabled: function(aCommand, dummy) + { + return true; + }, + doCommand: function(aCommand) + { + // In editor.js + CancelHTMLSource(); + } +}; diff --git a/mozilla/editor/ui/composer/content/EditorAllTags.css b/mozilla/editor/ui/composer/content/EditorAllTags.css index 49b4a80902f..319a1fa4b8c 100644 --- a/mozilla/editor/ui/composer/content/EditorAllTags.css +++ b/mozilla/editor/ui/composer/content/EditorAllTags.css @@ -239,6 +239,12 @@ div { background-position: top left; } +input div { + min-height: 0px; margin-left: 0px; margin-top: 0px; + padding-left: 0px; + background-image: none; +} + dl { min-height: 20px; margin-left: 2px; margin-top: 2px; padding-left: 17px; diff --git a/mozilla/editor/ui/composer/content/EditorContextMenu.js b/mozilla/editor/ui/composer/content/EditorContextMenu.js index 5b00f2e39ba..d3d4bf0ccbb 100644 --- a/mozilla/editor/ui/composer/content/EditorContextMenu.js +++ b/mozilla/editor/ui/composer/content/EditorContextMenu.js @@ -95,9 +95,14 @@ function ShowHiddenItemOnCleanup( id ) function ShowMenuItem(id, showItem) { var item = document.getElementById(id); - var showing = (item.getAttribute('hidden') !='true'); - if(item && (showItem != showing)) - item.setAttribute('hidden', showItem ? '' : 'true'); + if (item) + { + var showing = (item.getAttribute('hidden') !='true'); + if(showItem != showing) + item.setAttribute('hidden', showItem ? '' : 'true'); + } + else + dump("ShowMenuItem: item id="+id+" not found\n"); } function IsMenuItemShowing(menuID) diff --git a/mozilla/editor/ui/composer/content/editor.js b/mozilla/editor/ui/composer/content/editor.js index 4ac635a2800..649ec82ec61 100644 --- a/mozilla/editor/ui/composer/content/editor.js +++ b/mozilla/editor/ui/composer/content/editor.js @@ -45,6 +45,8 @@ var gContentWindow = 0; var gSourceContentWindow = 0; var gContentWindowDeck; var gFormatToolbar; +var gFormatToolbarHidden; +var gFormatToolbarCollapsed; var gEditModeBar; // Bummer! Can't get at enums from nsIDocumentEncoder.h var gOutputSelectionOnly = 1; @@ -116,12 +118,13 @@ var DocumentStateListener = EditorInitToolbars(); DoRecentFilesMenuSave(); // Save the recent files menu - // udpate menu items now that we have an editor to play with - dump("Updating 'create' commands\n"); + BuildRecentMenu(); window._content.focus(); + // udpate menu items now that we have an editor to play with + // Note: This must be AFTER window._content.focus(); + dump("Updating 'create' commands\n"); window.updateCommands("create"); - BuildRecentMenu(); }, NotifyDocumentWillBeDestroyed: function() {}, @@ -221,7 +224,7 @@ function EditorSharedStartup() document.getElementById("menu_DeleteCellContents").setAttribute("acceltext",DelStr); // hide UI that we don't have components for - HideInapplicableUIElements(); + RemoveInapplicableUIElements(); } function _EditorNotImplemented() @@ -400,6 +403,7 @@ function EditorCanClose() { // Returns FALSE only if user cancels save action dump("Calling EditorCanClose\n"); + return editorShell.CheckAndSaveDocument(GetString("BeforeClosing")); } @@ -720,6 +724,7 @@ function EditorRemoveLinks() // but will accept a parent table cell if inside one function GetSelectedElementOrParentCell() { +//dump("GetSelectedElementOrParentCell\n"); var element = editorShell.GetSelectedElement(""); if (!element) element = editorShell.GetElementOrParentByTagName("td",null); @@ -727,8 +732,6 @@ function GetSelectedElementOrParentCell() return element; } -// --------------------------- Dialogs --------------------------- - function EditorAlign(commandID, alignType) { var commandNode = document.getElementById(commandID); @@ -770,7 +773,6 @@ function SetEditMode(mode) { gSourceContentWindow.setAttribute("value",editorShell.GetContentsAs("text/html", gOutputBodyOnly)); gSourceContentWindow.focus(); - setTimeout("gSourceContentWindow.focus()", 10); return; } } @@ -790,19 +792,50 @@ function SetEditMode(mode) editorShell.editorSelection.collapse(bodyNode, 0); window._content.focus(); - // yuck. what is this? - setTimeout("window._content.focus()", 10); } } } -function CancelSourceEditing() +function CancelHTMLSource() { - // Empty the source window - gSourceContentWindow.value=""; +dump("*** CancelHTMLSource ***\n"); + // Don't convert source text back into the DOM document + gSourceContentWindow.value = ""; SetDisplayMode(PreviousNonSourceDisplayMode); } + +function FinishHTMLSource() +{ +dump("*** FinishHTMLSource ***\n"); + // Switch edit modes -- converts source back into DOM document + SetEditMode(PreviousNonSourceDisplayMode); +} + +function CollapseItem(id, collapse) +{ + var item = document.getElementById(id); + if (item) + { + if(collapse != (item.getAttribute("collapsed") == "true")) + item.setAttribute("collapsed", collapse ? "true" : ""); + } + else + dump("CollapseItem: item id="+id+" not found\n"); +} + +function DisableItem(id, disable) +{ + var item = document.getElementById(id); + if (item) + { + if(disable != (item.getAttribute("disabled") == "true")) + item.setAttribute("disabled", disable ? "true" : ""); + } + else + dump("DisableItem: item id="+id+" not found\n"); +} + function SetDisplayMode(mode) { if (gIsHTMLEditor) @@ -833,8 +866,36 @@ function SetDisplayMode(mode) // Switch to the sourceWindow (second in the deck) gContentWindowDeck.setAttribute("index","1"); - // TODO: WE MUST DISABLE APPROPRIATE COMMANDS - // and change UI to appropriate + // Hide menus that are completely disabled + // Note: ShowMenuItem is implemented in EditorContextMenu.js + ShowMenuItem("editMenu", false); + ShowMenuItem("viewMenu", false); + ShowMenuItem("insertMenu", false); + ShowMenuItem("formatMenu", false); + ShowMenuItem("tableMenu", false); + +/* + CollapseItem("editMenu", true); + CollapseItem("viewMenu", true); + CollapseItem("insertMenu", true); + CollapseItem("formatMenu", true); + CollapseItem("tableMenu", true); +*/ + + DisableItem("findButton", true); + DisableItem("spellingButton", true); + DisableItem("imageButton", true); + DisableItem("hlineButton", true); + DisableItem("tableButton", true); + DisableItem("linkButton", true); + DisableItem("namedAnchorButton", true); + + //Hide the formating toolbar if not already hidden + gFormatToolbarHidden = gFormatToolbar.getAttribute("hidden"); + if (gFormatToolbarHidden != "true") + { + gFormatToolbar.setAttribute("hidden", "true"); + } // THIS DOESN'T WORK! gSourceContentWindow.focus(); @@ -844,8 +905,33 @@ function SetDisplayMode(mode) // Switch to the normal editor (first in the deck) gContentWindowDeck.setAttribute("index","0"); - // TODO: WE MUST ENABLE APPROPRIATE COMMANDS - // and change UI back to "normal" + // Restore menus and toolbars + ShowMenuItem("editMenu", true); + ShowMenuItem("viewMenu", true); + ShowMenuItem("insertMenu", true); + ShowMenuItem("formatMenu", true); + ShowMenuItem("tableMenu", true); + +/* + CollapseItem("editMenu", false); + CollapseItem("viewMenu", false); + CollapseItem("insertMenu", false); + CollapseItem("formatMenu", false); + CollapseItem("tableMenu", false); +*/ + DisableItem("findButton", false); + DisableItem("spellingButton", false); + DisableItem("imageButton", false); + DisableItem("hlineButton", false); + DisableItem("tableButton", false); + DisableItem("linkButton", false); + DisableItem("namedAnchorButton", false); + + if (gFormatToolbarHidden != "true") + { +dump("Switching back to visible toolbar. gFormatToolbarHidden = "+gFormatToolbarHidden+"\n"); + gFormatToolbar.setAttribute("hidden", gFormatToolbarHidden); + } window._content.focus(); } @@ -1112,23 +1198,23 @@ function EditorInitFormatMenu() if (element && element.nodeName) { var objStr = ""; - menuItem.removeAttribute("disabled"); + menuItem.setAttribute("disabled", ""); var name = element.nodeName.toLowerCase(); switch (name) { - case 'img': + case "img": objStr = GetString("Image"); break; - case 'hr': + case "hr": objStr = GetString("HLine"); break; - case 'table': + case "table": objStr = GetString("Table"); break; - case 'td': + case "td": objStr = GetString("TableCell"); break; - case 'a': + case "a": if (element.name) objStr = GetString("NamedAnchor"); else if(element.href) @@ -1583,7 +1669,7 @@ function getColorAndSetColorWell(ColorPickerID, ColorWellID) if (colorPicker) { // Extract color from colorPicker and assign to colorWell. - var color = colorPicker.getAttribute('color'); + var color = colorPicker.getAttribute("color"); dump("setColor to: "+color+"\n"); if (colorWell && color) @@ -1608,11 +1694,12 @@ function IsSpellCheckerInstalled() var spellcheckerClass = Components.classes["mozilla.spellchecker.1"]; gHaveSpellChecker = (spellcheckerClass != null); gSoughtSpellChecker = true; + dump("Have SpellChecker = "+gHaveSpellChecker+"\n"); return gHaveSpellChecker; } //----------------------------------------------------------------------------------- -function HideInapplicableUIElements() +function RemoveInapplicableUIElements() { // if no spell checker, remove spell checker ui if (!IsSpellCheckerInstalled()) @@ -1633,7 +1720,34 @@ function HideInapplicableUIElements() if (spellingSepItem) spellingSepItem.parentNode.removeChild(spellingSepItem); } +/* + // if no spell checker, remove spell checker ui + if (!IsSpellCheckerInstalled()) + { + dump("Removing spell checker items\n"); + + // Completely remove UI elements intended for spelling + // This allows HTMLSource to use "hidden" to turn items off/on + // and also needed because of class="hide-in-IM" CSS rule used by AIM + // First, remove the command node used by both button and menuitem + var spellingCommand = document.getElementById("cmd_spelling"); + if (spellingCommand) + spellingCommand.parentNode.removeChild(spellingCommand); + + var spellingButton = document.getElementById("spellingButton"); + if (spellingButton) + spellingButton.parentNode.removeChild(spellingButton); + + var spellingMenuItem = document.getElementById("menu_checkspelling"); + if (spellingMenuItem) + spellingMenuItem.parentNode.removeChild(spellingMenuItem); + + var spellingSepItem = document.getElementById("sep_checkspelling"); + if (spellingSepItem) + spellingSepItem.parentNode.removeChild(spellingSepItem); + } +*/ } function onEditorFocus() @@ -1645,7 +1759,7 @@ function GetPrefsService() { // Store the prefs object try { - var prefsService = Components.classes['component://netscape/preferences']; + var prefsService = Components.classes["component://netscape/preferences"]; if (prefsService) prefsService = prefsService.getService(); if (prefsService) diff --git a/mozilla/editor/ui/composer/content/editorOverlay.xul b/mozilla/editor/ui/composer/content/editorOverlay.xul index 0cec7f5e536..6a4e994f629 100644 --- a/mozilla/editor/ui/composer/content/editorOverlay.xul +++ b/mozilla/editor/ui/composer/content/editorOverlay.xul @@ -602,10 +602,12 @@ +