From ed99ef899d48a6931e821998d4e4c71fe7a11e2c Mon Sep 17 00:00:00 2001 From: "jfrancis%netscape.com" Date: Wed, 24 May 2000 23:00:24 +0000 Subject: [PATCH] fixes: 14753, 29843, 39864, 40141, 40139, 36679, 39542, 34729, 34855, 37216, 39292, 26447 r=sfraser,cmanske,fm; a=beppe git-svn-id: svn://10.0.0.236/trunk@70759 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/editor/base/TypeInState.cpp | 25 + mozilla/editor/base/TypeInState.h | 6 + mozilla/editor/base/nsComposerCommands.cpp | 209 +++++-- mozilla/editor/base/nsComposerCommands.h | 58 +- mozilla/editor/base/nsEditor.cpp | 17 + mozilla/editor/base/nsEditor.h | 1 + mozilla/editor/base/nsEditorController.cpp | 2 + mozilla/editor/base/nsHTMLEditRules.cpp | 567 +++++++++++++----- mozilla/editor/base/nsHTMLEditRules.h | 23 +- mozilla/editor/base/nsHTMLEditUtils.cpp | 29 +- mozilla/editor/base/nsHTMLEditUtils.h | 3 +- mozilla/editor/base/nsHTMLEditor.cpp | 187 ++++-- mozilla/editor/base/nsHTMLEditor.h | 20 +- mozilla/editor/base/nsTextEditRules.cpp | 62 +- mozilla/editor/base/nsTextEditRules.h | 3 +- .../composer/src/nsComposerCommands.cpp | 209 +++++-- .../editor/composer/src/nsComposerCommands.h | 58 +- mozilla/editor/libeditor/base/nsEditor.cpp | 17 + mozilla/editor/libeditor/base/nsEditor.h | 1 + .../libeditor/base/nsEditorController.cpp | 2 + mozilla/editor/libeditor/html/TypeInState.cpp | 25 + mozilla/editor/libeditor/html/TypeInState.h | 6 + .../editor/libeditor/html/nsHTMLEditRules.cpp | 567 +++++++++++++----- .../editor/libeditor/html/nsHTMLEditRules.h | 23 +- .../editor/libeditor/html/nsHTMLEditUtils.cpp | 29 +- .../editor/libeditor/html/nsHTMLEditUtils.h | 3 +- .../editor/libeditor/html/nsHTMLEditor.cpp | 187 ++++-- mozilla/editor/libeditor/html/nsHTMLEditor.h | 20 +- .../editor/libeditor/text/nsTextEditRules.cpp | 62 +- .../editor/libeditor/text/nsTextEditRules.h | 3 +- mozilla/editor/public/nsIHTMLEditor.h | 49 +- mozilla/editor/ui/composer/content/editor.js | 161 +++-- .../ui/composer/content/editorOverlay.xul | 19 +- .../composer/locale/en-US/editor.properties | 3 + 34 files changed, 1960 insertions(+), 696 deletions(-) diff --git a/mozilla/editor/base/TypeInState.cpp b/mozilla/editor/base/TypeInState.cpp index 8a308ec26eb..44f7a650234 100644 --- a/mozilla/editor/base/TypeInState.cpp +++ b/mozilla/editor/base/TypeInState.cpp @@ -57,6 +57,7 @@ TypeInState::QueryInterface(REFNSIID aIID, void** aInstancePtr) TypeInState::TypeInState() : mSetArray() ,mClearedArray() +,mRelativeFontSize(0) { NS_INIT_REFCNT(); Reset(); @@ -108,6 +109,18 @@ nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr) nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue) { + // special case for big/small, these nest + if (nsIEditProperty::big == aProp) + { + mRelativeFontSize++; + return NS_OK; + } + if (nsIEditProperty::small == aProp) + { + mRelativeFontSize--; + return NS_OK; + } + // if it's already set we are done if (IsPropSet(aProp,aAttr,aValue)) return NS_OK; @@ -196,6 +209,17 @@ nsresult TypeInState::TakeSetProperty(PropItem **outPropItem) return NS_OK; } +//************************************************************************** +// TakeRelativeFontSize: hands back relative font value, which is then +// cleared out. +nsresult TypeInState::TakeRelativeFontSize(PRInt32 *outRelSize) +{ + if (!outRelSize) return NS_ERROR_NULL_POINTER; + *outRelSize = mRelativeFontSize; + mRelativeFontSize = 0; + return NS_OK; +} + nsresult TypeInState::GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp) { return GetTypingState(isSet, theSetting, aProp, nsAutoString(), nsAutoString()); @@ -248,6 +272,7 @@ nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp, if (!aProp) { // clear _all_ props + mRelativeFontSize=0; while ((index = mSetArray.Count())) { // go backwards to keep nsVoidArray from memmoving everything each time diff --git a/mozilla/editor/base/TypeInState.h b/mozilla/editor/base/TypeInState.h index 2299680f9da..eed5f33b7b6 100644 --- a/mozilla/editor/base/TypeInState.h +++ b/mozilla/editor/base/TypeInState.h @@ -69,6 +69,11 @@ public: // caller assumes ownership of PropItem and must delete it. nsresult TakeSetProperty(PropItem **outPropItem); + //************************************************************************** + // TakeRelativeFontSize: hands back relative font value, which is then + // cleared out. + nsresult TakeRelativeFontSize(PRInt32 *outRelSize); + nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp); nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp, const nsString &aAttr); @@ -87,6 +92,7 @@ protected: nsVoidArray mSetArray; nsVoidArray mClearedArray; + PRInt32 mRelativeFontSize; }; diff --git a/mozilla/editor/base/nsComposerCommands.cpp b/mozilla/editor/base/nsComposerCommands.cpp index 3962a194b73..85c0212ffa4 100644 --- a/mozilla/editor/base/nsComposerCommands.cpp +++ b/mozilla/editor/base/nsComposerCommands.cpp @@ -165,40 +165,6 @@ nsBaseStateUpdatingCommand::UpdateCommandState(const PRUnichar *aCommandName, ns } -#ifdef XP_MAC -#pragma mark - -#endif - - -NS_IMETHODIMP -nsAlwaysEnabledCommands::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) -{ - nsCOMPtr editorShell = do_QueryInterface(refCon); - *outCmdEnabled = (editorShell.get() != nsnull); // enabled if we have an editorShell - return NS_OK; -} - - -NS_IMETHODIMP -nsAlwaysEnabledCommands::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) -{ - nsCOMPtr editorShell = do_QueryInterface(refCon); - if (!editorShell) return NS_ERROR_NULL_POINTER; - - nsresult rv = NS_OK; - - nsAutoString cmdString(aCommand); - - /* - if (cmdString.EqualsWithConversion("cmd_scrollTop")) - return selCont->CompleteScroll(PR_FALSE); - else if (cmdString.EqualsWithConversion("cmd_scrollBottom")) - return selCont->CompleteScroll(PR_TRUE); - */ - - return rv; -} - #ifdef XP_MAC #pragma mark - #endif @@ -522,13 +488,13 @@ nsOutdentCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refC { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); - if (editor) + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (htmlEditor) { - // XXX fix me. You can't outdent if you're already at the top level. - //PRBool canOutdent; - //editor->CanIndent("outdent", &canOutdent); + PRBool canIndent, canOutdent; + htmlEditor->GetIndentState(canIndent, canOutdent); - *outCmdEnabled = PR_TRUE; + *outCmdEnabled = canOutdent; } } @@ -555,8 +521,21 @@ nsOutdentCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) #pragma mark - #endif + +nsMultiStateCommand::nsMultiStateCommand() +: nsBaseComposerCommand() +, mGotState(PR_FALSE) +{ +} + +nsMultiStateCommand::~nsMultiStateCommand() +{ +} + +NS_IMPL_ISUPPORTS_INHERITED1(nsMultiStateCommand, nsBaseComposerCommand, nsIStateUpdatingControllerCommand); + NS_IMETHODIMP -nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) +nsMultiStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; @@ -566,34 +545,166 @@ nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports editorShell->GetEditor(getter_AddRefs(editor)); if (editor) { + // should be disabled sometimes, like if the current selection is an image *outCmdEnabled = PR_TRUE; } } - - return NS_OK; + + return UpdateCommandState(aCommand, refCon); } NS_IMETHODIMP -nsParagraphStateCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) +nsMultiStateCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { - // we have to grab the state attribute on our command node to find out - // what format to set the paragraph to - nsAutoString stateAttribute; - rv = GetCommandNodeState(aCommand, editorShell, stateAttribute); - if (NS_FAILED(rv)) return rv; - rv = editorShell->SetParagraphFormat(stateAttribute.GetUnicode()); + // we have to grab the state attribute on our command node to find out + // what format to set the paragraph to + nsAutoString stateAttribute; + rv = GetCommandNodeState(aCommand, editorShell, stateAttribute); + if (NS_FAILED(rv)) return rv; + + rv = SetState(editorShell, stateAttribute); } return rv; } +NS_IMETHODIMP +nsMultiStateCommand::UpdateCommandState(const PRUnichar *aCommandName, nsISupports * refCon) +{ + nsCOMPtr editorShell = do_QueryInterface(refCon); + nsresult rv = NS_OK; + if (editorShell) + { + nsString curFormat; + PRBool isMixed; + + rv = GetCurrentState(editorShell, curFormat, isMixed); + if (NS_FAILED(rv)) return rv; + + if (isMixed) + curFormat.AssignWithConversion("mixed"); + + if (!mGotState || (curFormat != mStateString)) + { + // poke the UI + SetCommandNodeState(aCommandName, editorShell, curFormat); + + mGotState = PR_TRUE; + mStateString = curFormat; + } + } + return rv; +} + + +#ifdef XP_MAC +#pragma mark - +#endif + +nsParagraphStateCommand::nsParagraphStateCommand() +: nsMultiStateCommand() +{ +} + +nsresult +nsParagraphStateCommand::GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed) +{ + NS_ASSERTION(aEditorShell, "Need an editor shell here"); + + nsCOMPtr editor; + aEditorShell->GetEditor(getter_AddRefs(editor)); + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (!htmlEditor) return NS_ERROR_FAILURE; + + return htmlEditor->GetParagraphState(outMixed, outStateString); +} + + +nsresult +nsParagraphStateCommand::SetState(nsIEditorShell *aEditorShell, nsString& newState) +{ + NS_ASSERTION(aEditorShell, "Need an editor shell here"); + + nsCOMPtr editor; + aEditorShell->GetEditor(getter_AddRefs(editor)); + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (!htmlEditor) return NS_ERROR_FAILURE; + + return htmlEditor->SetParagraphFormat(newState); +} + + +#ifdef XP_MAC +#pragma mark - +#endif + +nsFontFaceStateCommand::nsFontFaceStateCommand() +: nsMultiStateCommand() +{ +} + +nsresult +nsFontFaceStateCommand::GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed) +{ + NS_ASSERTION(aEditorShell, "Need an editor shell here"); + + nsCOMPtr editor; + aEditorShell->GetEditor(getter_AddRefs(editor)); + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (!htmlEditor) return NS_ERROR_FAILURE; + + return htmlEditor->GetFontFaceState(outMixed, outStateString); +} + + +nsresult +nsFontFaceStateCommand::SetState(nsIEditorShell *aEditorShell, nsString& newState) +{ + NS_ASSERTION(aEditorShell, "Need an editor shell here"); + + nsCOMPtr editor; + aEditorShell->GetEditor(getter_AddRefs(editor)); + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (!htmlEditor) return NS_ERROR_FAILURE; + + nsresult rv; + + NS_ConvertASCIItoUCS2 emptyString(""); + NS_ConvertASCIItoUCS2 fontString("font"); + NS_ConvertASCIItoUCS2 faceString("face"); + + nsCOMPtr ttAtom = getter_AddRefs(NS_NewAtom("tt")); + nsCOMPtr fontAtom = getter_AddRefs(NS_NewAtom("font")); + + if (newState.EqualsWithConversion("tt")) + { + // The old "teletype" attribute + rv = htmlEditor->SetInlineProperty(ttAtom, &emptyString, &emptyString); + // Clear existing font face + rv = htmlEditor->RemoveInlineProperty(fontAtom, &faceString); + } + else + { + // Remove any existing TT nodes + rv = htmlEditor->RemoveInlineProperty(ttAtom, &emptyString); + + if (newState == emptyString || newState.EqualsWithConversion("normal")) { + rv = htmlEditor->RemoveInlineProperty(fontAtom, &faceString); + } else { + rv = htmlEditor->SetInlineProperty(fontAtom, &faceString, &newState); + } + } + + return rv; +} + #ifdef XP_MAC #pragma mark - #endif diff --git a/mozilla/editor/base/nsComposerCommands.h b/mozilla/editor/base/nsComposerCommands.h index 673ac9a8edc..1d145e68fc7 100644 --- a/mozilla/editor/base/nsComposerCommands.h +++ b/mozilla/editor/base/nsComposerCommands.h @@ -66,7 +66,7 @@ public: \ NS_DECL_NSICONTROLLERCOMMAND \ }; -// virtual base class for commands that need to save and update state +// virtual base class for commands that need to save and update Boolean state (like styles etc) class nsBaseStateUpdatingCommand : public nsBaseComposerCommand, public nsIStateUpdatingControllerCommand { @@ -98,7 +98,8 @@ protected: }; -// shared class for the various style updating commands +// Shared class for the various style updating commands like bold, italics etc. +// Suitable for commands whose state is either 'on' or 'off'. class nsStyleUpdatingCommand : public nsBaseStateUpdatingCommand { public: @@ -132,8 +133,58 @@ protected: }; +// Base class for commands whose state consists of a string (e.g. para format) +class nsMultiStateCommand : public nsBaseComposerCommand, + public nsIStateUpdatingControllerCommand +{ +public: + + nsMultiStateCommand(); + virtual ~nsMultiStateCommand(); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSICONTROLLERCOMMAND + NS_DECL_NSISTATEUPDATINGCONTROLLERCOMMAND + +protected: + + virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed) = 0; + virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState) = 0; + +protected: + + PRPackedBool mGotState; + nsString mStateString; + +}; + + +class nsParagraphStateCommand : public nsMultiStateCommand +{ +public: + nsParagraphStateCommand(); + +protected: + + virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed); + virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState); +}; + +class nsFontFaceStateCommand : public nsMultiStateCommand +{ +public: + nsFontFaceStateCommand(); + +protected: + + virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed); + virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState); +}; + + + + // composer commands -NS_DECL_COMPOSER_COMMAND(nsAlwaysEnabledCommands) NS_DECL_COMPOSER_COMMAND(nsCloseCommand) NS_DECL_COMPOSER_COMMAND(nsPrintingCommands) @@ -157,7 +208,6 @@ NS_DECL_COMPOSER_COMMAND(nsPasteQuotationCommand) NS_DECL_COMPOSER_COMMAND(nsIndentCommand) NS_DECL_COMPOSER_COMMAND(nsOutdentCommand) -NS_DECL_COMPOSER_COMMAND(nsParagraphStateCommand) NS_DECL_COMPOSER_COMMAND(nsAlignCommand) NS_DECL_COMPOSER_COMMAND(nsRemoveStylesCommand) NS_DECL_COMPOSER_COMMAND(nsIncreaseFontSizeCommand) diff --git a/mozilla/editor/base/nsEditor.cpp b/mozilla/editor/base/nsEditor.cpp index aa4e4101424..5c442047db5 100644 --- a/mozilla/editor/base/nsEditor.cpp +++ b/mozilla/editor/base/nsEditor.cpp @@ -3695,6 +3695,23 @@ nsEditor::NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag) return PR_FALSE; } +PRBool +nsEditor::NodeIsType(nsIDOMNode *aNode, const nsString &aTagStr) +{ + nsCOMPtrelement; + element = do_QueryInterface(aNode); + if (element) + { + nsAutoString tag; + element->GetTagName(tag); + if (tag.EqualsIgnoreCase(aTagStr)) + { + return PR_TRUE; + } + } + return PR_FALSE; +} + PRBool nsEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aChildTag) { diff --git a/mozilla/editor/base/nsEditor.h b/mozilla/editor/base/nsEditor.h index 9045dbb1246..88b9bc33900 100644 --- a/mozilla/editor/base/nsEditor.h +++ b/mozilla/editor/base/nsEditor.h @@ -598,6 +598,7 @@ public: /** returns PR_TRUE if aNode is of the type implied by aTag */ static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag); + static PRBool NodeIsType(nsIDOMNode *aNode, const nsString &aTag); /** returns PR_TRUE if aParent can contain a child of type aTag */ PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag); diff --git a/mozilla/editor/base/nsEditorController.cpp b/mozilla/editor/base/nsEditorController.cpp index 966015c71b4..9f16643032b 100644 --- a/mozilla/editor/base/nsEditorController.cpp +++ b/mozilla/editor/base/nsEditorController.cpp @@ -327,6 +327,8 @@ nsresult nsComposerController::RegisterComposerCommands(nsIControllerCommandMana // format stuff NS_REGISTER_ONE_COMMAND(nsParagraphStateCommand, "cmd_paragraphState"); + NS_REGISTER_ONE_COMMAND(nsFontFaceStateCommand, "cmd_fontFace"); + NS_REGISTER_ONE_COMMAND(nsAlignCommand, "cmd_align"); NS_REGISTER_ONE_COMMAND(nsRemoveStylesCommand, "cmd_removeStyles"); NS_REGISTER_ONE_COMMAND(nsIncreaseFontSizeCommand, "cmd_increaseFont"); diff --git a/mozilla/editor/base/nsHTMLEditRules.cpp b/mozilla/editor/base/nsHTMLEditRules.cpp index 67b4ccfbe8f..27a7d118172 100644 --- a/mozilla/editor/base/nsHTMLEditRules.cpp +++ b/mozilla/editor/base/nsHTMLEditRules.cpp @@ -79,7 +79,6 @@ nsHTMLEditRules::nsHTMLEditRules() : mDocChangeRange(nsnull) ,mListenerEnabled(PR_TRUE) ,mUtilRange(nsnull) -,mBody(nsnull) ,mJoinOffset(0) { } @@ -192,73 +191,8 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) nsresult res = NS_OK; if (!--mActionNesting) { - ConfirmSelectionInBody(); - if (action == nsEditor::kOpIgnore) return NS_OK; - - nsCOMPtrselection; - res = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - - if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) - { - // dont let any txns in here move the selection around behind our back. - // Note that this won't prevent explicit selection setting from working. - nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); - - // expand the "changed doc range" as needed - res = PromoteRange(mDocChangeRange, action); - if (NS_FAILED(res)) return res; - - // add in any needed
s, and remove any unneeded ones. - res = AdjustSpecialBreaks(); - if (NS_FAILED(res)) return res; - - // merge any adjacent text nodes - if ( (action != nsEditor::kOpInsertText && - action != nsEditor::kOpInsertIMEText) ) - { - res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange); - if (NS_FAILED(res)) return res; - } - - // adjust whitespace for insert text and delete actions - if ((action == nsEditor::kOpInsertText) || - (action == nsEditor::kOpInsertIMEText) || - (action == nsEditor::kOpDeleteSelection)) - { - res = AdjustWhitespace(selection); - if (NS_FAILED(res)) return res; - } - - // replace newlines that are preformatted - // MOOSE: This is buttUgly. A better way to - // organize the action enum is in order. - if ((action == nsEditor::kOpInsertText) || - (action == nsEditor::kOpInsertIMEText) || - (action == nsHTMLEditor::kOpInsertElement) || - (action == nsHTMLEditor::kOpInsertQuotation) || - (action == nsEditor::kOpInsertNode)) - { - res = ReplaceNewlines(mDocChangeRange); - } - - // clean up any empty nodes in the selection - res = RemoveEmptyNodes(); - if (NS_FAILED(res)) return res; - - // adjust selection for insert text and delete actions - if ((action == nsEditor::kOpInsertText) || - (action == nsEditor::kOpInsertIMEText) || - (action == nsEditor::kOpDeleteSelection)) - { - res = AdjustSelection(selection, aDirection); - if (NS_FAILED(res)) return res; - } - } - - // detect empty doc - res = CreateBogusNodeIfNeeded(selection); - + // do all the tricky stuff + res = AfterEditInner(action, aDirection); // turn on caret nsCOMPtr selCon; mEditor->GetSelectionController(getter_AddRefs(selCon)); @@ -269,6 +203,79 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) } +nsresult +nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection) +{ + ConfirmSelectionInBody(); + if (action == nsEditor::kOpIgnore) return NS_OK; + + nsCOMPtrselection; + nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) + { + // dont let any txns in here move the selection around behind our back. + // Note that this won't prevent explicit selection setting from working. + nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); + + // expand the "changed doc range" as needed + res = PromoteRange(mDocChangeRange, action); + if (NS_FAILED(res)) return res; + + // add in any needed
s, and remove any unneeded ones. + res = AdjustSpecialBreaks(); + if (NS_FAILED(res)) return res; + + // merge any adjacent text nodes + if ( (action != nsEditor::kOpInsertText && + action != nsEditor::kOpInsertIMEText) ) + { + res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange); + if (NS_FAILED(res)) return res; + } + + // adjust whitespace for insert text and delete actions + if ((action == nsEditor::kOpInsertText) || + (action == nsEditor::kOpInsertIMEText) || + (action == nsEditor::kOpDeleteSelection)) + { + res = AdjustWhitespace(selection); + if (NS_FAILED(res)) return res; + } + + // replace newlines that are preformatted + // MOOSE: This is buttUgly. A better way to + // organize the action enum is in order. + if ((action == nsEditor::kOpInsertText) || + (action == nsEditor::kOpInsertIMEText) || + (action == nsHTMLEditor::kOpInsertElement) || + (action == nsHTMLEditor::kOpInsertQuotation) || + (action == nsEditor::kOpInsertNode)) + { + res = ReplaceNewlines(mDocChangeRange); + } + + // clean up any empty nodes in the selection + res = RemoveEmptyNodes(); + if (NS_FAILED(res)) return res; + + // adjust selection for insert text and delete actions + if ((action == nsEditor::kOpInsertText) || + (action == nsEditor::kOpInsertIMEText) || + (action == nsEditor::kOpDeleteSelection)) + { + res = AdjustSelection(selection, aDirection); + if (NS_FAILED(res)) return res; + } + } + + // detect empty doc + res = CreateBogusNodeIfNeeded(selection); + return res; +} + + NS_IMETHODIMP nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, @@ -303,7 +310,7 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, case kDeleteSelection: return WillDeleteSelection(aSelection, info->collapsedAction, aCancel, aHandled); case kMakeList: - return WillMakeList(aSelection, info->bOrdered, aCancel, aHandled); + return WillMakeList(aSelection, info->blockType, aCancel, aHandled); case kIndent: return WillIndent(aSelection, aCancel, aHandled); case kOutdent: @@ -314,6 +321,8 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled); case kRemoveList: return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled); + case kMakeDefListItem: + return WillMakeDefListItem(aSelection, info->blockType, aCancel, aHandled); case kInsertElement: return WillInsert(aSelection, aCancel); } @@ -349,10 +358,10 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL) PRBool bNonList = PR_FALSE; nsCOMPtr arrayOfNodes; - nsresult res = GetListActionNodes(&arrayOfNodes); + nsresult res = GetListActionNodes(&arrayOfNodes, PR_TRUE); if (NS_FAILED(res)) return res; - // yummy yummy yummy i've got nodes in my tummy + // examine list type for nodes in selection PRUint32 listCount; PRInt32 i; arrayOfNodes->Count(&listCount); @@ -389,7 +398,102 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL) NS_IMETHODIMP nsHTMLEditRules::GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent) { - return NS_ERROR_FAILURE; + aCanIndent = PR_TRUE; + aCanOutdent = PR_FALSE; + + nsCOMPtr arrayOfNodes; + nsresult res = GetListActionNodes(&arrayOfNodes, PR_TRUE); + if (NS_FAILED(res)) return res; + + // examine nodes in selection for blockquotes or list elements; + // these we can outdent. Note that we return true for canOutdent + // if *any* of the selection is outdentable, rather than all of it. + PRUint32 listCount; + PRInt32 i; + arrayOfNodes->Count(&listCount); + for (i=(PRInt32)listCount-1; i>=0; i--) + { + nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + if (nsHTMLEditUtils::IsList(curNode) || + nsHTMLEditUtils::IsListItem(curNode) || + nsHTMLEditUtils::IsBlockquote(curNode)) + { + aCanOutdent = PR_TRUE; + break; + } + } + + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::GetParagraphState(PRBool &aMixed, nsString &outFormat) +{ + // This routine is *heavily* tied to our ui choices in the paragraph + // style popup. I cant see a way around that. + aMixed = PR_TRUE; + outFormat.AssignWithConversion(""); + + PRBool bMixed = PR_FALSE; + nsAutoString formatStr; + // using "x" as anuninitialized value, since "" is meaningful + formatStr.AssignWithConversion("x"); + + nsCOMPtr arrayOfNodes; + nsresult res = GetParagraphFormatNodes(&arrayOfNodes, PR_TRUE); + if (NS_FAILED(res)) return res; + + // loop through the nodes in selection and examine their paragraph format + PRUint32 listCount; + PRInt32 i; + arrayOfNodes->Count(&listCount); + for (i=(PRInt32)listCount-1; i>=0; i--) + { + nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + nsAutoString format; + nsCOMPtr atom = mEditor->GetTag(curNode); + + if (mEditor->IsInlineNode(curNode)) + format.AssignWithConversion(""); + else if (nsIEditProperty::p == atom) + format.AssignWithConversion("P"); + else if (nsHTMLEditUtils::IsHeader(curNode)) + { + nsAutoString tag; + nsEditor::GetTagString(curNode,tag); + tag.ToUpperCase(); + format = tag; + } + else if (nsIEditProperty::blockquote == atom) + format.AssignWithConversion("BLOCKQUOTE"); + else if (nsIEditProperty::address == atom) + format.AssignWithConversion("ADDRESS"); + else if (nsIEditProperty::pre == atom) + format.AssignWithConversion("PRE"); + else if (nsIEditProperty::dt == atom) + format.AssignWithConversion("DT"); + else if (nsIEditProperty::dd == atom) + format.AssignWithConversion("DD"); + + // if this is the first node, we've found, remember it as the format + if (formatStr.EqualsWithConversion("x")) + formatStr = format; + // else make sure it matches previously found format + else if (format != formatStr) + { + bMixed = PR_TRUE; + break; + } + } + + aMixed = bMixed; + outFormat = formatStr; + return res; } @@ -825,19 +929,30 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, } } } - // is prior node inline and same type? (This shouldn't happen) - if ( mEditor->HasSameBlockNodeParent(node, priorNode) && - ( mEditor->IsInlineNode(node) || mEditor->IsTextNode(node) ) && - mEditor->NodesSameType(node, priorNode) ) + // is prior node a text node? + else if ( mEditor->IsTextNode(priorNode) ) { - // if so, join them! - nsCOMPtr topParent; - priorNode->GetParentNode(getter_AddRefs(topParent)); - *aHandled = PR_TRUE; - res = JoinNodesSmart(priorNode,node,&selNode,&selOffset); + // delete last character + nsCOMPtrnodeAsText; + nodeAsText = do_QueryInterface(priorNode); + nodeAsText->GetLength((PRUint32*)&offset); + res = aSelection->Collapse(priorNode,offset); + // just return without setting handled to true. + // default code will take care of actual deletion + return res; + } + else if ( mEditor->IsInlineNode(priorNode) ) + { + // remember where we are + res = mEditor->GetNodeLocation(priorNode, &node, &offset); if (NS_FAILED(res)) return res; + // delete it + res = mEditor->DeleteNode(priorNode); + if (NS_FAILED(res)) return res; + // we did something, so lets say so. + *aHandled = PR_TRUE; // fix up selection - res = aSelection->Collapse(selNode,selOffset); + res = aSelection->Collapse(node,offset); return res; } else return NS_OK; // punt to default @@ -924,19 +1039,30 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, } } } - // is next node inline and same type? (This shouldn't happen) - if ( mEditor->HasSameBlockNodeParent(node, nextNode) && - ( mEditor->IsInlineNode(node) || mEditor->IsTextNode(node) ) && - mEditor->NodesSameType(node, nextNode) ) + // is next node a text node? + else if ( mEditor->IsTextNode(nextNode) ) { - // if so, join them! - nsCOMPtr topParent; - nextNode->GetParentNode(getter_AddRefs(topParent)); - *aHandled = PR_TRUE; - res = JoinNodesSmart(node,nextNode,&selNode,&selOffset); + // delete last character + nsCOMPtrnodeAsText; + nodeAsText = do_QueryInterface(nextNode); + nodeAsText->GetLength((PRUint32*)&offset); + res = aSelection->Collapse(nextNode,offset); + // just return without setting handled to true. + // default code will take care of actual deletion + return res; + } + else if ( mEditor->IsInlineNode(nextNode) ) + { + // remember where we are + res = mEditor->GetNodeLocation(nextNode, &node, &offset); if (NS_FAILED(res)) return res; + // delete it + res = mEditor->DeleteNode(nextNode); + if (NS_FAILED(res)) return res; + // we did something, so lets say so. + *aHandled = PR_TRUE; // fix up selection - res = aSelection->Collapse(selNode,selOffset); + res = aSelection->Collapse(node,offset); return res; } else return NS_OK; // punt to default @@ -1001,6 +1127,11 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; if (!nodeToDelete) return NS_ERROR_NULL_POINTER; + if (mBody == nodeToDelete) + { + *aCancel = PR_TRUE; + return res; + } // if this node is text node, adjust selection if (nsEditor::IsTextNode(nodeToDelete)) @@ -1153,28 +1284,32 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsresult nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, - PRBool aOrdered, + const nsString *aListType, PRBool *aCancel, - PRBool *aHandled) + PRBool *aHandled, + const nsString *aItemType) { - if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } + if (!aSelection || !aListType || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } nsresult res = WillInsert(aSelection, aCancel); if (NS_FAILED(res)) return res; - nsAutoString listType; - listType.AssignWithConversion( aOrdered ? "ol" : "ul" ); - // initialize out param // we want to ignore result of WillInsert() *aCancel = PR_FALSE; *aHandled = PR_FALSE; - nsAutoString blockType; - blockType.AssignWithConversion( aOrdered ? "ol" : "ul"); + // deduce what tag to use for list items + nsAutoString itemType; + if (aItemType) + itemType = *aItemType; + else if (aListType->EqualsWithConversion("dl")) + itemType.AssignWithConversion("dd"); + else + itemType.AssignWithConversion("li"); PRBool outMakeEmpty; - res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty); + res = ShouldMakeEmptyBlock(aSelection, aListType, &outMakeEmpty); if (NS_FAILED(res)) return res; if (outMakeEmpty) { @@ -1186,11 +1321,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; // make sure we can put a list here - res = SplitAsNeeded(&listType, &parent, &offset); + res = SplitAsNeeded(aListType, &parent, &offset); if (NS_FAILED(res)) return res; - res = mEditor->CreateNode(listType, parent, offset, getter_AddRefs(theList)); + res = mEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList)); if (NS_FAILED(res)) return res; - res = mEditor->CreateNode(NS_ConvertASCIItoUCS2("li"), theList, 0, getter_AddRefs(theListItem)); + res = mEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem)); if (NS_FAILED(res)) return res; // put selection in new list item res = aSelection->Collapse(theListItem,0); @@ -1224,8 +1359,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, nsCOMPtr curNode( do_QueryInterface(isupports) ); while (nsHTMLEditUtils::IsDiv(curNode) - || nsHTMLEditUtils::IsOrderedList(curNode) - || nsHTMLEditUtils::IsUnorderedList(curNode) + || nsHTMLEditUtils::IsList(curNode) || nsHTMLEditUtils::IsBlockquote(curNode)) { // dive as long as there is only one child, and it is a list, div, blockquote @@ -1238,8 +1372,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, // keep diving nsCOMPtr tmpNode = nsEditor::GetChildAt(curNode, 0); if (nsHTMLEditUtils::IsDiv(tmpNode) - || nsHTMLEditUtils::IsOrderedList(tmpNode) - || nsHTMLEditUtils::IsUnorderedList(tmpNode) + || nsHTMLEditUtils::IsList(tmpNode) || nsHTMLEditUtils::IsBlockquote(tmpNode)) { // check editablility XXX floppy moose @@ -1305,37 +1438,38 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, if (nsHTMLEditUtils::IsList(curNode)) { + nsAutoString existingListStr; + res = mEditor->GetTagString(curNode, existingListStr); // do we have a curList already? if (curList && !nsHTMLEditUtils::IsDescendantOf(curNode, curList)) { - // move all of our dachildren into curList. + // move all of our children into curList. // cheezy way to do it: move whole list and then - // RemoveContainer() on the list + // RemoveContainer() on the list. + // ConvertListType first: that routine + // handles converting the list item types, if needed res = mEditor->MoveNode(curNode, curList, -1); if (NS_FAILED(res)) return res; - res = mEditor->RemoveContainer(curNode); + res = ConvertListType(curNode, &newBlock, *aListType, itemType); if (NS_FAILED(res)) return res; - } - else if ( (nsHTMLEditUtils::IsUnorderedList(curNode) && aOrdered) || - (nsHTMLEditUtils::IsOrderedList(curNode) && !aOrdered) ) - - { - // replace list with new list type - res = mEditor->ReplaceContainer(curNode,&newBlock,blockType); + res = mEditor->RemoveContainer(newBlock); if (NS_FAILED(res)) return res; - curList = newBlock; } else { - curList = curNode; + // replace list with new list type + res = ConvertListType(curNode, &newBlock, *aListType, itemType); + if (NS_FAILED(res)) return res; + curList = newBlock; } continue; } if (nsHTMLEditUtils::IsListItem(curNode)) { - if ( (nsHTMLEditUtils::IsUnorderedList(curParent) && aOrdered) || - (nsHTMLEditUtils::IsOrderedList(curParent) && !aOrdered) ) + nsAutoString existingListStr; + res = mEditor->GetTagString(curParent, existingListStr); + if ( existingListStr != *aListType ) { // list item is in wrong type of list. // if we dont have a curList, split the old list @@ -1348,16 +1482,23 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRInt32 o; res = nsEditor::GetNodeLocation(curParent, &p, &o); if (NS_FAILED(res)) return res; - res = mEditor->CreateNode(listType, p, o, getter_AddRefs(curList)); + res = mEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList)); if (NS_FAILED(res)) return res; } // move list item to new list res = mEditor->MoveNode(curNode, curList, -1); if (NS_FAILED(res)) return res; + // convert list item type if needed + if (!mEditor->NodeIsType(curNode,itemType)) + { + res = mEditor->ReplaceContainer(curNode, &newBlock, itemType); + if (NS_FAILED(res)) return res; + } } else { // item is in right type of list. But we might still have to move it. + // and we might need to convert list item types. if (!curList) curList = curParent; else @@ -1369,6 +1510,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; } } + if (!mEditor->NodeIsType(curNode,itemType)) + { + res = mEditor->ReplaceContainer(curNode, &newBlock, itemType); + if (NS_FAILED(res)) return res; + } } continue; } @@ -1377,9 +1523,9 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, // or if this node doesn't go in list we used earlier. if (!curList) // || transitionList[i]) { - res = SplitAsNeeded(&listType, &curParent, &offset); + res = SplitAsNeeded(aListType, &curParent, &offset); if (NS_FAILED(res)) return res; - res = mEditor->CreateNode(listType, curParent, offset, getter_AddRefs(curList)); + res = mEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList)); if (NS_FAILED(res)) return res; // curList is now the correct thing to put curNode in prevListItem = 0; @@ -1404,11 +1550,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, // don't wrap li around a paragraph. instead replace paragraph with li if (nsHTMLEditUtils::IsParagraph(curNode)) { - res = mEditor->ReplaceContainer(curNode, &listItem, NS_ConvertASCIItoUCS2("li")); + res = mEditor->ReplaceContainer(curNode, &listItem, itemType); } else { - res = mEditor->InsertContainerAbove(curNode, &listItem, NS_ConvertASCIItoUCS2("li")); + res = mEditor->InsertContainerAbove(curNode, &listItem, itemType); } if (NS_FAILED(res)) return res; if (nsEditor::IsInlineNode(curNode)) @@ -1525,6 +1671,18 @@ nsHTMLEditRules::WillRemoveList(nsIDOMSelection *aSelection, } +nsresult +nsHTMLEditRules::WillMakeDefListItem(nsIDOMSelection *aSelection, + const nsString *aItemType, + PRBool *aCancel, + PRBool *aHandled) +{ + // for now we let WillMakeList handle this + nsAutoString listType; + listType.AssignWithConversion("dl"); + return WillMakeList(aSelection, &listType, aCancel, aHandled, aItemType); +} + nsresult nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, @@ -1808,6 +1966,40 @@ nsHTMLEditRules::WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBoo } +/////////////////////////////////////////////////////////////////////////// +// ConvertListType: convert list type and list item type. +// +// +nsresult +nsHTMLEditRules::ConvertListType(nsIDOMNode *aList, + nsCOMPtr *outList, + const nsString& aListType, + const nsString& aItemType) +{ + if (!aList || !outList) return NS_ERROR_NULL_POINTER; + *outList = aList; // we migvht not need to change the list + nsresult res = NS_OK; + nsCOMPtr child, temp; + aList->GetFirstChild(getter_AddRefs(child)); + while (child) + { + if (!mEditor->NodeIsType(child, aItemType)) + { + res = mEditor->ReplaceContainer(child, &temp, aItemType); + if (NS_FAILED(res)) return res; + child = temp; + } + child->GetNextSibling(getter_AddRefs(temp)); + child = temp; + } + if (!mEditor->NodeIsType(aList, aListType)) + { + res = mEditor->ReplaceContainer(aList, outList, aListType); + } + return res; +} + + /////////////////////////////////////////////////////////////////////////// // CreateStyleForInsertText: take care of clearing and setting appropriate // style nodes for text insertion. @@ -1839,10 +2031,15 @@ nsHTMLEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDoc } // then process setting any styles - mEditor->mTypeInState->TakeSetProperty(&item); + PRInt32 relFontSize; - if (item) // we have at least one style to add; make a - { // new text node to insert style nodes above. + res = mEditor->mTypeInState->TakeRelativeFontSize(&relFontSize); + if (NS_FAILED(res)) return res; + res = mEditor->mTypeInState->TakeSetProperty(&item); + if (NS_FAILED(res)) return res; + + if (item || relFontSize) // we have at least one style to add; make a + { // new text node to insert style nodes above. if (mEditor->IsTextNode(node)) { // if we are in a text node, split it @@ -1863,17 +2060,29 @@ nsHTMLEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDoc node = newNode; offset = 0; weDidSometing = PR_TRUE; + + if (relFontSize) + { + PRInt32 j, dir; + // dir indicated bigger versus smaller. 1 = bigger, -1 = smaller + if (relFontSize > 0) dir=1; + else dir = -1; + for (j=0; jRelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1); + if (NS_FAILED(res)) return res; + } + } + + while (item) + { + res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value); + if (NS_FAILED(res)) return res; + // we own item now (TakeSetProperty hands ownership to us) + delete item; + mEditor->mTypeInState->TakeSetProperty(&item); + } } - - while (item) - { - res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value); - if (NS_FAILED(res)) return res; - // we own item now (TakeSetProperty hands ownership to us) - delete item; - mEditor->mTypeInState->TakeSetProperty(&item); - } - if (weDidSometing) return aSelection->Collapse(node, offset); @@ -2616,7 +2825,8 @@ nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange, nsresult nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges, nsCOMPtr *outArrayOfNodes, - PRInt32 inOperationType) + PRInt32 inOperationType, + PRBool aDontTouchContent) { if (!inArrayOfRanges || !outArrayOfNodes) return NS_ERROR_NULL_POINTER; @@ -2669,7 +2879,7 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges, { nsCOMPtr isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i)); nsCOMPtr node( do_QueryInterface(isupports) ); - if (mEditor->IsInlineNode(node) && mEditor->IsContainer(node)) + if (!aDontTouchContent && mEditor->IsInlineNode(node) && mEditor->IsContainer(node)) { nsCOMPtr arrayOfInlines; res = BustUpInlinesAtBRs(node, &arrayOfInlines); @@ -2735,7 +2945,8 @@ nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode, // GetListActionNodes: // nsresult -nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes) +nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes, + PRBool aDontTouchContent) { if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER; @@ -2748,7 +2959,58 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes) if (NS_FAILED(res)) return res; // use these ranges to contruct a list of nodes to act on. - res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList); + res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList, aDontTouchContent); + if (NS_FAILED(res)) return res; + + // pre process our list of nodes... + PRUint32 listCount; + PRInt32 i; + (*outArrayOfNodes)->Count(&listCount); + for (i=(PRInt32)listCount-1; i>=0; i--) + { + nsCOMPtr isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i)); + nsCOMPtr testNode( do_QueryInterface(isupports ) ); + + // Remove all non-editable nodes. Leave them be. + if (!mEditor->IsEditable(testNode)) + { + (*outArrayOfNodes)->RemoveElementAt(i); + } + + // scan for table elements. If we find table elements other than table, + // replace it with a list of any editable non-table content. + if (mEditor->IsTableElement(testNode) && !mEditor->IsTable(testNode)) + { + (*outArrayOfNodes)->RemoveElementAt(i); + nsCOMPtr arrayOfTableContent; + res = GetTableContent(testNode, &arrayOfTableContent); + if (NS_FAILED(res)) return res; + (*outArrayOfNodes)->AppendElements(arrayOfTableContent); + } + } + return res; +} + + +/////////////////////////////////////////////////////////////////////////// +// GetParagraphFormatNodes: +// +nsresult +nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr *outArrayOfNodes, + PRBool aDontTouchContent) +{ + if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER; + + nsCOMPtrselection; + nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + nsCOMPtr arrayOfRanges; + res = GetPromotedRanges(selection, &arrayOfRanges, kMakeList); + if (NS_FAILED(res)) return res; + + // use these ranges to contruct a list of nodes to act on. + res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeBasicBlock, aDontTouchContent); if (NS_FAILED(res)) return res; // pre process our list of nodes... @@ -3285,7 +3547,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, // Note that if _nothing_ should happen, ie, the selection is // already entireyl inside a block (or blocks) or the correct type, // then you don't want to return true in outMakeEmpty, since the - // defualt code will insert a new empty block anyway, rather than + // default code will insert a new empty block anyway, rather than // doing nothing. So we have to detect that case and return false. if (!aSelection || !outMakeEmpty) return NS_ERROR_NULL_POINTER; @@ -3342,18 +3604,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, *outMakeEmpty = PR_FALSE; return res; } - - // in an empty block? - PRBool bIsEmptyNode; - // the PR_TRUE param tell IsEmptyNode to not count moz-BRs as content - res = IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE); - if (bIsEmptyNode) - { - // we must be in a text or inline node - convert existing block - *outMakeEmpty = PR_TRUE; - return res; - } - + // is it after a
with no inline nodes after it, or a
after it?? if (offset) { @@ -3383,7 +3634,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, // we are after a
and not before inline content, // or we are between
s. // make an empty block - *outMakeEmpty = PR_FALSE; + *outMakeEmpty = PR_TRUE; return res; } } diff --git a/mozilla/editor/base/nsHTMLEditRules.h b/mozilla/editor/base/nsHTMLEditRules.h index 1b66e084ee2..92027abc924 100644 --- a/mozilla/editor/base/nsHTMLEditRules.h +++ b/mozilla/editor/base/nsHTMLEditRules.h @@ -55,6 +55,7 @@ public: // nsIHTMLEditRules methods NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL); NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent); + NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFormat); // nsIEditActionListener methods @@ -97,11 +98,12 @@ protected: nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction, PRBool *aCancel, PRBool *aHandled); - nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); + nsresult WillMakeList(nsIDOMSelection *aSelection, const nsString *aListType, PRBool *aCancel, PRBool *aHandled, const nsString *aItemType=nsnull); nsresult WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel, PRBool *aHandled); + nsresult WillMakeDefListItem(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled); nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled); nsresult DidMakeBasicBlock(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); @@ -115,17 +117,13 @@ protected: nsresult ReturnInParagraph(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled); nsresult ReturnInListItem(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset); + nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection); + nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr *outList, const nsString& aListType, const nsString& aItemType); nsresult CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDocument *aDoc); nsresult IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock, PRBool aMozBRDoesntCount = PR_FALSE, PRBool aListItemsNotEmpty = PR_FALSE); -#if 0 - nsresult IsEmptyNode(nsIDOMNode *aNode, - PRBool *outIsEmptyBlock, - PRBool aMozBRDoesntCount = PR_FALSE, - PRBool aListItemsNotEmpty = PR_FALSE); -#endif PRBool IsFirstNode(nsIDOMNode *aNode); PRBool IsLastNode(nsIDOMNode *aNode); PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock); @@ -138,11 +136,13 @@ protected: PRInt32 inOperationType); nsresult PromoteRange(nsIDOMRange *inRange, PRInt32 inOperationType); nsresult GetNodesForOperation(nsISupportsArray *inArrayOfRanges, - nsCOMPtr *outArrayOfNodes, - PRInt32 inOperationType); + nsCOMPtr *outArrayOfNodes, + PRInt32 inOperationType, + PRBool aDontTouchContent=PR_FALSE); nsresult GetChildNodesForOperation(nsIDOMNode *inNode, - nsCOMPtr *outArrayOfNodes); - nsresult GetListActionNodes(nsCOMPtr *outArrayOfNodes); + nsCOMPtr *outArrayOfNodes); + nsresult GetListActionNodes(nsCOMPtr *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE); + nsresult GetParagraphFormatNodes(nsCOMPtr *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE); nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode, nsCOMPtr *outArrayOfNodes); nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, @@ -185,7 +185,6 @@ protected: nsCOMPtr mDocChangeRange; PRBool mListenerEnabled; nsCOMPtr mUtilRange; - nsCOMPtr mBody; PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin... }; diff --git a/mozilla/editor/base/nsHTMLEditUtils.cpp b/mozilla/editor/base/nsHTMLEditUtils.cpp index d57310c355b..aea8d6cc470 100644 --- a/mozilla/editor/base/nsHTMLEditUtils.cpp +++ b/mozilla/editor/base/nsHTMLEditUtils.cpp @@ -220,7 +220,9 @@ nsHTMLEditUtils::IsListItem(nsIDOMNode *node) nsAutoString tag; nsEditor::GetTagString(node,tag); tag.ToLowerCase(); - if (tag.EqualsWithConversion("li")) + if (tag.EqualsWithConversion("li") || + tag.EqualsWithConversion("dd") || + tag.EqualsWithConversion("dt")) { return PR_TRUE; } @@ -289,7 +291,8 @@ nsHTMLEditUtils::IsList(nsIDOMNode *node) nsAutoString tag; nsEditor::GetTagString(node,tag); tag.ToLowerCase(); - if ( (tag.EqualsWithConversion("ol")) || + if ( (tag.EqualsWithConversion("dl")) || + (tag.EqualsWithConversion("ol")) || (tag.EqualsWithConversion("ul")) ) { return PR_TRUE; @@ -299,7 +302,7 @@ nsHTMLEditUtils::IsList(nsIDOMNode *node) /////////////////////////////////////////////////////////////////////////// -// IsOrderedList: true if node an html orderd list +// IsOrderedList: true if node an html ordered list // PRBool nsHTMLEditUtils::IsOrderedList(nsIDOMNode *node) @@ -317,7 +320,7 @@ nsHTMLEditUtils::IsOrderedList(nsIDOMNode *node) /////////////////////////////////////////////////////////////////////////// -// IsUnorderedList: true if node an html orderd list +// IsUnorderedList: true if node an html unordered list // PRBool nsHTMLEditUtils::IsUnorderedList(nsIDOMNode *node) @@ -334,6 +337,24 @@ nsHTMLEditUtils::IsUnorderedList(nsIDOMNode *node) } +/////////////////////////////////////////////////////////////////////////// +// IsDefinitionList: true if node an html definition list +// +PRBool +nsHTMLEditUtils::IsDefinitionList(nsIDOMNode *node) +{ + NS_PRECONDITION(node, "null parent passed to nsHTMLEditUtils::IsDefinitionList"); + nsAutoString tag; + nsEditor::GetTagString(node,tag); + tag.ToLowerCase(); + if (tag.EqualsWithConversion("dl")) + { + return PR_TRUE; + } + return PR_FALSE; +} + + /////////////////////////////////////////////////////////////////////////// // IsBlockquote: true if node an html blockquote node // diff --git a/mozilla/editor/base/nsHTMLEditUtils.h b/mozilla/editor/base/nsHTMLEditUtils.h index e956cbf16c6..2c96510cf81 100644 --- a/mozilla/editor/base/nsHTMLEditUtils.h +++ b/mozilla/editor/base/nsHTMLEditUtils.h @@ -48,8 +48,9 @@ public: static PRBool IsTableRow(nsIDOMNode *aNode); static PRBool IsTableCell(nsIDOMNode *aNode); static PRBool IsList(nsIDOMNode *aNode); - static PRBool IsUnorderedList(nsIDOMNode *aNode); static PRBool IsOrderedList(nsIDOMNode *aNode); + static PRBool IsUnorderedList(nsIDOMNode *aNode); + static PRBool IsDefinitionList(nsIDOMNode *aNode); static PRBool IsBlockquote(nsIDOMNode *aNode); static PRBool IsPre(nsIDOMNode *aNode); static PRBool IsAddress(nsIDOMNode *aNode); diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp index c6330c7f1cd..40a7915aa22 100644 --- a/mozilla/editor/base/nsHTMLEditor.cpp +++ b/mozilla/editor/base/nsHTMLEditor.cpp @@ -1001,6 +1001,10 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) if (!outBRNode) return NS_ERROR_NULL_POINTER; *outBRNode = nsnull; + + // calling it text insertion to trigger moz br treatment by rules + nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext); + nsresult res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; res = selection->GetIsCollapsed(&bCollapsed); @@ -1019,6 +1023,8 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) if (NS_FAILED(res)) return res; // position selection after br + res = GetNodeLocation(*outBRNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; selection->SetHint(PR_TRUE); res = selection->Collapse(selNode, selOffset+1); @@ -1127,22 +1133,28 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, if (NS_FAILED(res)) return res; // iterate range and build up array - iter->Init(range); - while (NS_ENUMERATOR_FALSE == iter->IsDone()) + res = iter->Init(range); + // init returns an error if no nodes in range. + // this can easily happen with the subtree + // iterator if the selection doesn't contain + // any *whole* nodes. + if (NS_SUCCEEDED(res)) { - res = iter->CurrentNode(getter_AddRefs(content)); - if (NS_FAILED(res)) return res; - node = do_QueryInterface(content); - if (!node) return NS_ERROR_FAILURE; - if (IsEditable(node)) - { - isupports = do_QueryInterface(node); - arrayOfNodes->AppendElement(isupports); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; } - res = iter->Next(); - if (NS_FAILED(res)) return res; } - // MOOSE: workaround for selection bug: //selection->ClearSelection(); @@ -1713,7 +1725,20 @@ PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset) NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue, - PRBool &aFirst, PRBool &aAny, PRBool &aAll) + PRBool &aFirst, + PRBool &aAny, + PRBool &aAll) +{ + return GetInlinePropertyWithAttrValue( aProperty, aAttribute, aValue, aFirst, aAny, aAll, nsnull); +} + +NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, + PRBool &aAny, + PRBool &aAll, + nsString *outValue) { if (!aProperty) return NS_ERROR_NULL_POINTER; @@ -1805,11 +1830,6 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, } return NS_OK; } - else if (aProperty == mFontAtom.get()) - { -// MOOSE! - return NS_OK; - } } // either non-collapsed selection or no cached value: do it the hard way @@ -1822,6 +1842,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, iter->Init(range); nsCOMPtr content; + nsAutoString firstValue, theValue; iter->CurrentNode(getter_AddRefs(content)); while (NS_ENUMERATOR_FALSE == iter->IsDone()) { @@ -1866,12 +1887,20 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, { PRBool isSet; nsCOMPtrresultNode; - IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode)); if (first) { + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &firstValue); aFirst = isSet; first = PR_FALSE; + if (outValue) *outValue = firstValue; } + else + { + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &theValue); + if (firstValue != theValue) + aAll = PR_FALSE; + } + if (isSet) { aAny = PR_TRUE; } @@ -2790,7 +2819,10 @@ nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat) { nsAutoString tag; tag.Assign(aParagraphFormat); tag.ToLowerCase(); - return InsertBasicBlock(tag); + if (tag.EqualsWithConversion("dd") || tag.EqualsWithConversion("dt")) + return MakeDefinitionItem(tag); + else + return InsertBasicBlock(tag); } // XXX: ERROR_HANDLING -- this method needs a little work to ensure all error codes are @@ -2907,20 +2939,52 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists) } -// get the paragraph style(s) for the selection NS_IMETHODIMP -nsHTMLEditor::GetParagraphTags(nsStringArray *aTagList) +nsHTMLEditor::GetParagraphState(PRBool &aMixed, nsString &outFormat) { -#if 0 - if (gNoisy) { printf("---------- nsHTMLEditor::GetPargraphTags ----------\n"); } -#endif - return GetParentBlockTags(aTagList, PR_FALSE); + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr htmlRules = do_QueryInterface(mRules); + if (!htmlRules) return NS_ERROR_FAILURE; + + return htmlRules->GetParagraphState(aMixed, outFormat); } NS_IMETHODIMP -nsHTMLEditor::GetListTags(nsStringArray *aTagList) +nsHTMLEditor::GetFontFaceState(PRBool &aMixed, nsString &outFace) { - return GetParentBlockTags(aTagList, PR_TRUE); + aMixed = PR_TRUE; + outFace.AssignWithConversion(""); + + nsresult res; + nsAutoString faceStr; faceStr.AssignWithConversion("face"); + PRBool first, any, all; + + res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &faceStr, nsnull, first, any, all, &outFace); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + return res; + } + + res = GetInlineProperty(nsIEditProperty::tt, nsnull, nsnull, first, any, all); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + nsIEditProperty::tt->ToString(outFace); + } + + if (!any) + { + // there was no font face attrs of any kind. We are in normal font. + outFace.AssignWithConversion(""); + aMixed = PR_FALSE; + } + return res; } NS_IMETHODIMP @@ -2934,6 +2998,17 @@ nsHTMLEditor::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL) return htmlRules->GetListState(aMixed, aOL, aUL); } +NS_IMETHODIMP +nsHTMLEditor::GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent) +{ + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr htmlRules = do_QueryInterface(mRules); + if (!htmlRules) return NS_ERROR_FAILURE; + + return htmlRules->GetIndentState(aCanIndent, aCanOutdent); +} + NS_IMETHODIMP nsHTMLEditor::MakeOrChangeList(const nsString& aListType) { @@ -2952,8 +3027,7 @@ nsHTMLEditor::MakeOrChangeList(const nsString& aListType) if (!selection) return NS_ERROR_NULL_POINTER; nsTextRulesInfo ruleInfo(nsTextEditRules::kMakeList); - if (aListType.EqualsWithConversion("ol")) ruleInfo.bOrdered = PR_TRUE; - else ruleInfo.bOrdered = PR_FALSE; + ruleInfo.blockType = &aListType; res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); if (cancel || (NS_FAILED(res))) return res; @@ -3041,8 +3115,37 @@ nsHTMLEditor::RemoveList(const nsString& aListType) return res; } +nsresult +nsHTMLEditor::MakeDefinitionItem(const nsString& aItemType) +{ + nsresult res; + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } -NS_IMETHODIMP + nsCOMPtr selection; + PRBool cancel, handled; + + nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpMakeDefListItem, nsIEditor::eNext); + + // pre-process + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + nsTextRulesInfo ruleInfo(nsTextEditRules::kMakeDefListItem); + ruleInfo.blockType = &aItemType; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (cancel || (NS_FAILED(res))) return res; + + if (!handled) + { + // todo: no default for now. we count on rules to handle it. + } + + res = mRules->DidDoAction(selection, &ruleInfo, res); + return res; +} + +nsresult nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType) { nsresult res; @@ -5534,7 +5637,8 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, const nsString *aAttribute, const nsString *aValue, PRBool &aIsSet, - nsIDOMNode **aStyleNode) const + nsIDOMNode **aStyleNode, + nsString *outValue) const { nsresult result; aIsSet = PR_FALSE; // must be initialized to false for code below to work @@ -5548,15 +5652,15 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, element = do_QueryInterface(node); if (element) { - nsAutoString tag; + nsAutoString tag, value; element->GetTagName(tag); if (propName.EqualsIgnoreCase(tag)) { PRBool found = PR_FALSE; if (aAttribute && 0!=aAttribute->Length()) { - nsAutoString value; element->GetAttribute(*aAttribute, value); + if (outValue) *outValue = value; if (value.Length()) { if (!aValue) { @@ -6182,12 +6286,14 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) res = selection->GetIsCollapsed(&bCollapsed); if (NS_FAILED(res)) return res; - // if it's collapsed dont do anything. - // MOOSE: We should probably have typing state for this like - // we do for other things. + // if it's collapsed set typing state if (bCollapsed) { - return NS_OK; + nsCOMPtr atom; + if (aSizeChange==1) atom = nsIEditProperty::big; + else atom = nsIEditProperty::small; + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + return mTypeInState->SetProp(atom, nsnull, nsnull); } // wrap with txn batching, rules sniffing, and selection preservation code @@ -6346,6 +6452,9 @@ nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, PRUint32 textLen; aTextNode->GetLength(&textLen); + // -1 is a magic value meaning to the end of node + if (aEndOffset == -1) aEndOffset = textLen; + if ( (PRUint32)aEndOffset != textLen ) { // we need to split off back of text node diff --git a/mozilla/editor/base/nsHTMLEditor.h b/mozilla/editor/base/nsHTMLEditor.h index bd9b4811f8a..da37e64dc4c 100644 --- a/mozilla/editor/base/nsHTMLEditor.h +++ b/mozilla/editor/base/nsHTMLEditor.h @@ -69,6 +69,7 @@ public: kOpAlign = 3004, kOpMakeBasicBlock = 3005, kOpRemoveList = 3006, + kOpMakeDefListItem = 3007, kOpInsertElement = 3008, kOpInsertQuotation = 3009 }; @@ -103,6 +104,11 @@ public: const nsString *aAttribute, const nsString *aValue, PRBool &aFirst, PRBool &aAny, PRBool &aAll); + NS_IMETHOD GetInlinePropertyWithAttrValue(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, PRBool &aAny, PRBool &aAll, + nsString *outValue); NS_IMETHOD RemoveAllInlineProperties(); NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute); @@ -125,13 +131,14 @@ public: NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat); NS_IMETHOD GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists); - NS_IMETHOD GetParagraphTags(nsStringArray *aTagList); - NS_IMETHOD GetListTags(nsStringArray *aTagList); + + NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFormat); + NS_IMETHOD GetFontFaceState(PRBool &aMixed, nsString &outFace); NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL); + NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent); NS_IMETHOD MakeOrChangeList(const nsString& aListType); NS_IMETHOD RemoveList(const nsString& aListType); - NS_IMETHOD InsertBasicBlock(const nsString& aBlockType); NS_IMETHOD Indent(const nsString& aIndent); NS_IMETHOD Align(const nsString& aAlign); @@ -441,7 +448,8 @@ protected: const nsString *aAttribute, const nsString *aValue, PRBool &aIsSet, - nsIDOMNode **aStyleNode) const; + nsIDOMNode **aStyleNode, + nsString *outValue = nsnull) const; /** style-based query returns PR_TRUE if (aProperty, aAttribute) is set in aSC. * WARNING: not well tested yet since we don't do style-based queries anywhere. @@ -473,6 +481,10 @@ protected: /* small utility routine to test the eEditorReadonly bit */ PRBool IsModifiable(); + /* helpers for block transformations */ + nsresult MakeDefinitionItem(const nsString& aItemType); + nsresult InsertBasicBlock(const nsString& aBlockType); + /* increase/decrease the font size of selection */ nsresult RelativeFontChange( PRInt32 aSizeChange); diff --git a/mozilla/editor/base/nsTextEditRules.cpp b/mozilla/editor/base/nsTextEditRules.cpp index b782ccbdd58..18baf0ce803 100644 --- a/mozilla/editor/base/nsTextEditRules.cpp +++ b/mozilla/editor/base/nsTextEditRules.cpp @@ -70,6 +70,9 @@ NS_NewTextEditRules(nsIEditRules** aInstancePtrResult) nsTextEditRules::nsTextEditRules() : mEditor(nsnull) , mFlags(0) // initialized to 0 ("no flags set"). Real initial value is given in Init() +, mPasswordText() +, mBogusNode(nsnull) +, mBody(nsnull) , mActionNesting(0) , mLockRulesSniffing(PR_FALSE) , mTheAction(0) @@ -106,29 +109,33 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) nsCOMPtr selection; mEditor->GetSelection(getter_AddRefs(selection)); NS_ASSERTION(selection, "editor cannot get selection"); - nsresult res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway - // create a range that is the entire body contents - if (NS_FAILED(res)) return res; + // remember our root node nsCOMPtr bodyElement; - res = mEditor->GetRootElement(getter_AddRefs(bodyElement)); + nsresult res = mEditor->GetRootElement(getter_AddRefs(bodyElement)); if (NS_FAILED(res)) return res; if (!bodyElement) return NS_ERROR_NULL_POINTER; - nsCOMPtrbodyNode = do_QueryInterface(bodyElement); - if (!bodyNode) return NS_ERROR_FAILURE; + mBody = do_QueryInterface(bodyElement); + if (!mBody) return NS_ERROR_FAILURE; + + // put in a magic br if needed + res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway + if (NS_FAILED(res)) return res; + + // create a range that is the entire body contents nsCOMPtr wholeDoc; res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), getter_AddRefs(wholeDoc)); if (NS_FAILED(res)) return res; - wholeDoc->SetStart(bodyNode,0); + wholeDoc->SetStart(mBody,0); nsCOMPtr list; - res = bodyNode->GetChildNodes(getter_AddRefs(list)); + res = mBody->GetChildNodes(getter_AddRefs(list)); if (NS_FAILED(res) || !list) return res?res:NS_ERROR_FAILURE; PRUint32 listCount; res = list->GetLength(&listCount); if (NS_FAILED(res)) return res; - res = wholeDoc->SetEnd(bodyNode,listCount); + res = wholeDoc->SetEnd(mBody,listCount); if (NS_FAILED(res)) return res; // replace newlines in that range with breaks @@ -1188,19 +1195,14 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection) // tell rules system to not do any post-processing nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone); - nsCOMPtr bodyElement; - - nsresult res = mEditor->GetRootElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(res)) return res; - if (!bodyElement) return NS_ERROR_NULL_POINTER; - nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + if (!mBody) return NS_ERROR_NULL_POINTER; // now we've got the body tag. // iterate the body tag, looking for editable content // if no editable content is found, insert the bogus node PRBool needsBogusContent=PR_TRUE; nsCOMPtrbodyChild; - res = bodyNode->GetFirstChild(getter_AddRefs(bodyChild)); + nsresult res = mBody->GetFirstChild(getter_AddRefs(bodyChild)); while ((NS_SUCCEEDED(res)) && bodyChild) { if (mEditor->IsMozEditorBogusNode(bodyChild) || mEditor->IsEditable(bodyChild)) @@ -1214,25 +1216,27 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection) } if (needsBogusContent) { - // set mBogusNode to be the newly created
- res = mEditor->CreateNode(NS_ConvertASCIItoUCS2("br"), bodyNode, 0, - getter_AddRefs(mBogusNode)); + // create a br + nsCOMPtr domDoc; + res = mEditor->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtrbrElement; + res = domDoc->CreateElement(NS_ConvertASCIItoUCS2("br"),getter_AddRefs(brElement)); if (NS_FAILED(res)) return res; + + // set mBogusNode to be the newly created
+ mBogusNode = do_QueryInterface(brElement); if (!mBogusNode) return NS_ERROR_NULL_POINTER; // give it a special attribute - nsCOMPtrnewPElement; - newPElement = do_QueryInterface(mBogusNode); - if (newPElement) - { - newPElement->SetAttribute( - NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeAttr), - NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeValue) - ); - } + brElement->SetAttribute( NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeAttr), + NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeValue) ); + // put the node in the document + res = mEditor->InsertNode(mBogusNode,mBody,0); + if (NS_FAILED(res)) return res; + // set selection - aSelection->Collapse(bodyNode,0); + aSelection->Collapse(mBody,0); } return res; } diff --git a/mozilla/editor/base/nsTextEditRules.h b/mozilla/editor/base/nsTextEditRules.h index b241f9c3970..9f1329be9a3 100644 --- a/mozilla/editor/base/nsTextEditRules.h +++ b/mozilla/editor/base/nsTextEditRules.h @@ -81,6 +81,7 @@ public: kAlign = 3004, kMakeBasicBlock = 3005, kRemoveList = 3006, + kMakeDefListItem = 3007, kInsertElement = 3008 }; @@ -173,6 +174,7 @@ protected: nsHTMLEditor *mEditor; // note that we do not refcount the editor nsString mPasswordText; // a buffer we use to store the real value of password editors nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc + nsCOMPtr mBody; // cached root node PRUint32 mFlags; PRUint32 mActionNesting; PRBool mLockRulesSniffing; @@ -195,7 +197,6 @@ class nsTextRulesInfo : public nsRulesInfo outputFormat(0), maxLength(-1), collapsedAction(nsIEditor::eNext), - bOrdered(PR_FALSE), alignType(0), blockType(0), insertElement(0) diff --git a/mozilla/editor/composer/src/nsComposerCommands.cpp b/mozilla/editor/composer/src/nsComposerCommands.cpp index 3962a194b73..85c0212ffa4 100644 --- a/mozilla/editor/composer/src/nsComposerCommands.cpp +++ b/mozilla/editor/composer/src/nsComposerCommands.cpp @@ -165,40 +165,6 @@ nsBaseStateUpdatingCommand::UpdateCommandState(const PRUnichar *aCommandName, ns } -#ifdef XP_MAC -#pragma mark - -#endif - - -NS_IMETHODIMP -nsAlwaysEnabledCommands::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) -{ - nsCOMPtr editorShell = do_QueryInterface(refCon); - *outCmdEnabled = (editorShell.get() != nsnull); // enabled if we have an editorShell - return NS_OK; -} - - -NS_IMETHODIMP -nsAlwaysEnabledCommands::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) -{ - nsCOMPtr editorShell = do_QueryInterface(refCon); - if (!editorShell) return NS_ERROR_NULL_POINTER; - - nsresult rv = NS_OK; - - nsAutoString cmdString(aCommand); - - /* - if (cmdString.EqualsWithConversion("cmd_scrollTop")) - return selCont->CompleteScroll(PR_FALSE); - else if (cmdString.EqualsWithConversion("cmd_scrollBottom")) - return selCont->CompleteScroll(PR_TRUE); - */ - - return rv; -} - #ifdef XP_MAC #pragma mark - #endif @@ -522,13 +488,13 @@ nsOutdentCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refC { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); - if (editor) + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (htmlEditor) { - // XXX fix me. You can't outdent if you're already at the top level. - //PRBool canOutdent; - //editor->CanIndent("outdent", &canOutdent); + PRBool canIndent, canOutdent; + htmlEditor->GetIndentState(canIndent, canOutdent); - *outCmdEnabled = PR_TRUE; + *outCmdEnabled = canOutdent; } } @@ -555,8 +521,21 @@ nsOutdentCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) #pragma mark - #endif + +nsMultiStateCommand::nsMultiStateCommand() +: nsBaseComposerCommand() +, mGotState(PR_FALSE) +{ +} + +nsMultiStateCommand::~nsMultiStateCommand() +{ +} + +NS_IMPL_ISUPPORTS_INHERITED1(nsMultiStateCommand, nsBaseComposerCommand, nsIStateUpdatingControllerCommand); + NS_IMETHODIMP -nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) +nsMultiStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; @@ -566,34 +545,166 @@ nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports editorShell->GetEditor(getter_AddRefs(editor)); if (editor) { + // should be disabled sometimes, like if the current selection is an image *outCmdEnabled = PR_TRUE; } } - - return NS_OK; + + return UpdateCommandState(aCommand, refCon); } NS_IMETHODIMP -nsParagraphStateCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) +nsMultiStateCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { - // we have to grab the state attribute on our command node to find out - // what format to set the paragraph to - nsAutoString stateAttribute; - rv = GetCommandNodeState(aCommand, editorShell, stateAttribute); - if (NS_FAILED(rv)) return rv; - rv = editorShell->SetParagraphFormat(stateAttribute.GetUnicode()); + // we have to grab the state attribute on our command node to find out + // what format to set the paragraph to + nsAutoString stateAttribute; + rv = GetCommandNodeState(aCommand, editorShell, stateAttribute); + if (NS_FAILED(rv)) return rv; + + rv = SetState(editorShell, stateAttribute); } return rv; } +NS_IMETHODIMP +nsMultiStateCommand::UpdateCommandState(const PRUnichar *aCommandName, nsISupports * refCon) +{ + nsCOMPtr editorShell = do_QueryInterface(refCon); + nsresult rv = NS_OK; + if (editorShell) + { + nsString curFormat; + PRBool isMixed; + + rv = GetCurrentState(editorShell, curFormat, isMixed); + if (NS_FAILED(rv)) return rv; + + if (isMixed) + curFormat.AssignWithConversion("mixed"); + + if (!mGotState || (curFormat != mStateString)) + { + // poke the UI + SetCommandNodeState(aCommandName, editorShell, curFormat); + + mGotState = PR_TRUE; + mStateString = curFormat; + } + } + return rv; +} + + +#ifdef XP_MAC +#pragma mark - +#endif + +nsParagraphStateCommand::nsParagraphStateCommand() +: nsMultiStateCommand() +{ +} + +nsresult +nsParagraphStateCommand::GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed) +{ + NS_ASSERTION(aEditorShell, "Need an editor shell here"); + + nsCOMPtr editor; + aEditorShell->GetEditor(getter_AddRefs(editor)); + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (!htmlEditor) return NS_ERROR_FAILURE; + + return htmlEditor->GetParagraphState(outMixed, outStateString); +} + + +nsresult +nsParagraphStateCommand::SetState(nsIEditorShell *aEditorShell, nsString& newState) +{ + NS_ASSERTION(aEditorShell, "Need an editor shell here"); + + nsCOMPtr editor; + aEditorShell->GetEditor(getter_AddRefs(editor)); + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (!htmlEditor) return NS_ERROR_FAILURE; + + return htmlEditor->SetParagraphFormat(newState); +} + + +#ifdef XP_MAC +#pragma mark - +#endif + +nsFontFaceStateCommand::nsFontFaceStateCommand() +: nsMultiStateCommand() +{ +} + +nsresult +nsFontFaceStateCommand::GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed) +{ + NS_ASSERTION(aEditorShell, "Need an editor shell here"); + + nsCOMPtr editor; + aEditorShell->GetEditor(getter_AddRefs(editor)); + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (!htmlEditor) return NS_ERROR_FAILURE; + + return htmlEditor->GetFontFaceState(outMixed, outStateString); +} + + +nsresult +nsFontFaceStateCommand::SetState(nsIEditorShell *aEditorShell, nsString& newState) +{ + NS_ASSERTION(aEditorShell, "Need an editor shell here"); + + nsCOMPtr editor; + aEditorShell->GetEditor(getter_AddRefs(editor)); + nsCOMPtr htmlEditor = do_QueryInterface(editor); + if (!htmlEditor) return NS_ERROR_FAILURE; + + nsresult rv; + + NS_ConvertASCIItoUCS2 emptyString(""); + NS_ConvertASCIItoUCS2 fontString("font"); + NS_ConvertASCIItoUCS2 faceString("face"); + + nsCOMPtr ttAtom = getter_AddRefs(NS_NewAtom("tt")); + nsCOMPtr fontAtom = getter_AddRefs(NS_NewAtom("font")); + + if (newState.EqualsWithConversion("tt")) + { + // The old "teletype" attribute + rv = htmlEditor->SetInlineProperty(ttAtom, &emptyString, &emptyString); + // Clear existing font face + rv = htmlEditor->RemoveInlineProperty(fontAtom, &faceString); + } + else + { + // Remove any existing TT nodes + rv = htmlEditor->RemoveInlineProperty(ttAtom, &emptyString); + + if (newState == emptyString || newState.EqualsWithConversion("normal")) { + rv = htmlEditor->RemoveInlineProperty(fontAtom, &faceString); + } else { + rv = htmlEditor->SetInlineProperty(fontAtom, &faceString, &newState); + } + } + + return rv; +} + #ifdef XP_MAC #pragma mark - #endif diff --git a/mozilla/editor/composer/src/nsComposerCommands.h b/mozilla/editor/composer/src/nsComposerCommands.h index 673ac9a8edc..1d145e68fc7 100644 --- a/mozilla/editor/composer/src/nsComposerCommands.h +++ b/mozilla/editor/composer/src/nsComposerCommands.h @@ -66,7 +66,7 @@ public: \ NS_DECL_NSICONTROLLERCOMMAND \ }; -// virtual base class for commands that need to save and update state +// virtual base class for commands that need to save and update Boolean state (like styles etc) class nsBaseStateUpdatingCommand : public nsBaseComposerCommand, public nsIStateUpdatingControllerCommand { @@ -98,7 +98,8 @@ protected: }; -// shared class for the various style updating commands +// Shared class for the various style updating commands like bold, italics etc. +// Suitable for commands whose state is either 'on' or 'off'. class nsStyleUpdatingCommand : public nsBaseStateUpdatingCommand { public: @@ -132,8 +133,58 @@ protected: }; +// Base class for commands whose state consists of a string (e.g. para format) +class nsMultiStateCommand : public nsBaseComposerCommand, + public nsIStateUpdatingControllerCommand +{ +public: + + nsMultiStateCommand(); + virtual ~nsMultiStateCommand(); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSICONTROLLERCOMMAND + NS_DECL_NSISTATEUPDATINGCONTROLLERCOMMAND + +protected: + + virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed) = 0; + virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState) = 0; + +protected: + + PRPackedBool mGotState; + nsString mStateString; + +}; + + +class nsParagraphStateCommand : public nsMultiStateCommand +{ +public: + nsParagraphStateCommand(); + +protected: + + virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed); + virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState); +}; + +class nsFontFaceStateCommand : public nsMultiStateCommand +{ +public: + nsFontFaceStateCommand(); + +protected: + + virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed); + virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState); +}; + + + + // composer commands -NS_DECL_COMPOSER_COMMAND(nsAlwaysEnabledCommands) NS_DECL_COMPOSER_COMMAND(nsCloseCommand) NS_DECL_COMPOSER_COMMAND(nsPrintingCommands) @@ -157,7 +208,6 @@ NS_DECL_COMPOSER_COMMAND(nsPasteQuotationCommand) NS_DECL_COMPOSER_COMMAND(nsIndentCommand) NS_DECL_COMPOSER_COMMAND(nsOutdentCommand) -NS_DECL_COMPOSER_COMMAND(nsParagraphStateCommand) NS_DECL_COMPOSER_COMMAND(nsAlignCommand) NS_DECL_COMPOSER_COMMAND(nsRemoveStylesCommand) NS_DECL_COMPOSER_COMMAND(nsIncreaseFontSizeCommand) diff --git a/mozilla/editor/libeditor/base/nsEditor.cpp b/mozilla/editor/libeditor/base/nsEditor.cpp index aa4e4101424..5c442047db5 100644 --- a/mozilla/editor/libeditor/base/nsEditor.cpp +++ b/mozilla/editor/libeditor/base/nsEditor.cpp @@ -3695,6 +3695,23 @@ nsEditor::NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag) return PR_FALSE; } +PRBool +nsEditor::NodeIsType(nsIDOMNode *aNode, const nsString &aTagStr) +{ + nsCOMPtrelement; + element = do_QueryInterface(aNode); + if (element) + { + nsAutoString tag; + element->GetTagName(tag); + if (tag.EqualsIgnoreCase(aTagStr)) + { + return PR_TRUE; + } + } + return PR_FALSE; +} + PRBool nsEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aChildTag) { diff --git a/mozilla/editor/libeditor/base/nsEditor.h b/mozilla/editor/libeditor/base/nsEditor.h index 9045dbb1246..88b9bc33900 100644 --- a/mozilla/editor/libeditor/base/nsEditor.h +++ b/mozilla/editor/libeditor/base/nsEditor.h @@ -598,6 +598,7 @@ public: /** returns PR_TRUE if aNode is of the type implied by aTag */ static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag); + static PRBool NodeIsType(nsIDOMNode *aNode, const nsString &aTag); /** returns PR_TRUE if aParent can contain a child of type aTag */ PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag); diff --git a/mozilla/editor/libeditor/base/nsEditorController.cpp b/mozilla/editor/libeditor/base/nsEditorController.cpp index 966015c71b4..9f16643032b 100644 --- a/mozilla/editor/libeditor/base/nsEditorController.cpp +++ b/mozilla/editor/libeditor/base/nsEditorController.cpp @@ -327,6 +327,8 @@ nsresult nsComposerController::RegisterComposerCommands(nsIControllerCommandMana // format stuff NS_REGISTER_ONE_COMMAND(nsParagraphStateCommand, "cmd_paragraphState"); + NS_REGISTER_ONE_COMMAND(nsFontFaceStateCommand, "cmd_fontFace"); + NS_REGISTER_ONE_COMMAND(nsAlignCommand, "cmd_align"); NS_REGISTER_ONE_COMMAND(nsRemoveStylesCommand, "cmd_removeStyles"); NS_REGISTER_ONE_COMMAND(nsIncreaseFontSizeCommand, "cmd_increaseFont"); diff --git a/mozilla/editor/libeditor/html/TypeInState.cpp b/mozilla/editor/libeditor/html/TypeInState.cpp index 8a308ec26eb..44f7a650234 100644 --- a/mozilla/editor/libeditor/html/TypeInState.cpp +++ b/mozilla/editor/libeditor/html/TypeInState.cpp @@ -57,6 +57,7 @@ TypeInState::QueryInterface(REFNSIID aIID, void** aInstancePtr) TypeInState::TypeInState() : mSetArray() ,mClearedArray() +,mRelativeFontSize(0) { NS_INIT_REFCNT(); Reset(); @@ -108,6 +109,18 @@ nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr) nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue) { + // special case for big/small, these nest + if (nsIEditProperty::big == aProp) + { + mRelativeFontSize++; + return NS_OK; + } + if (nsIEditProperty::small == aProp) + { + mRelativeFontSize--; + return NS_OK; + } + // if it's already set we are done if (IsPropSet(aProp,aAttr,aValue)) return NS_OK; @@ -196,6 +209,17 @@ nsresult TypeInState::TakeSetProperty(PropItem **outPropItem) return NS_OK; } +//************************************************************************** +// TakeRelativeFontSize: hands back relative font value, which is then +// cleared out. +nsresult TypeInState::TakeRelativeFontSize(PRInt32 *outRelSize) +{ + if (!outRelSize) return NS_ERROR_NULL_POINTER; + *outRelSize = mRelativeFontSize; + mRelativeFontSize = 0; + return NS_OK; +} + nsresult TypeInState::GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp) { return GetTypingState(isSet, theSetting, aProp, nsAutoString(), nsAutoString()); @@ -248,6 +272,7 @@ nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp, if (!aProp) { // clear _all_ props + mRelativeFontSize=0; while ((index = mSetArray.Count())) { // go backwards to keep nsVoidArray from memmoving everything each time diff --git a/mozilla/editor/libeditor/html/TypeInState.h b/mozilla/editor/libeditor/html/TypeInState.h index 2299680f9da..eed5f33b7b6 100644 --- a/mozilla/editor/libeditor/html/TypeInState.h +++ b/mozilla/editor/libeditor/html/TypeInState.h @@ -69,6 +69,11 @@ public: // caller assumes ownership of PropItem and must delete it. nsresult TakeSetProperty(PropItem **outPropItem); + //************************************************************************** + // TakeRelativeFontSize: hands back relative font value, which is then + // cleared out. + nsresult TakeRelativeFontSize(PRInt32 *outRelSize); + nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp); nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp, const nsString &aAttr); @@ -87,6 +92,7 @@ protected: nsVoidArray mSetArray; nsVoidArray mClearedArray; + PRInt32 mRelativeFontSize; }; diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp index 67b4ccfbe8f..27a7d118172 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp @@ -79,7 +79,6 @@ nsHTMLEditRules::nsHTMLEditRules() : mDocChangeRange(nsnull) ,mListenerEnabled(PR_TRUE) ,mUtilRange(nsnull) -,mBody(nsnull) ,mJoinOffset(0) { } @@ -192,73 +191,8 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) nsresult res = NS_OK; if (!--mActionNesting) { - ConfirmSelectionInBody(); - if (action == nsEditor::kOpIgnore) return NS_OK; - - nsCOMPtrselection; - res = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) return res; - - if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) - { - // dont let any txns in here move the selection around behind our back. - // Note that this won't prevent explicit selection setting from working. - nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); - - // expand the "changed doc range" as needed - res = PromoteRange(mDocChangeRange, action); - if (NS_FAILED(res)) return res; - - // add in any needed
s, and remove any unneeded ones. - res = AdjustSpecialBreaks(); - if (NS_FAILED(res)) return res; - - // merge any adjacent text nodes - if ( (action != nsEditor::kOpInsertText && - action != nsEditor::kOpInsertIMEText) ) - { - res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange); - if (NS_FAILED(res)) return res; - } - - // adjust whitespace for insert text and delete actions - if ((action == nsEditor::kOpInsertText) || - (action == nsEditor::kOpInsertIMEText) || - (action == nsEditor::kOpDeleteSelection)) - { - res = AdjustWhitespace(selection); - if (NS_FAILED(res)) return res; - } - - // replace newlines that are preformatted - // MOOSE: This is buttUgly. A better way to - // organize the action enum is in order. - if ((action == nsEditor::kOpInsertText) || - (action == nsEditor::kOpInsertIMEText) || - (action == nsHTMLEditor::kOpInsertElement) || - (action == nsHTMLEditor::kOpInsertQuotation) || - (action == nsEditor::kOpInsertNode)) - { - res = ReplaceNewlines(mDocChangeRange); - } - - // clean up any empty nodes in the selection - res = RemoveEmptyNodes(); - if (NS_FAILED(res)) return res; - - // adjust selection for insert text and delete actions - if ((action == nsEditor::kOpInsertText) || - (action == nsEditor::kOpInsertIMEText) || - (action == nsEditor::kOpDeleteSelection)) - { - res = AdjustSelection(selection, aDirection); - if (NS_FAILED(res)) return res; - } - } - - // detect empty doc - res = CreateBogusNodeIfNeeded(selection); - + // do all the tricky stuff + res = AfterEditInner(action, aDirection); // turn on caret nsCOMPtr selCon; mEditor->GetSelectionController(getter_AddRefs(selCon)); @@ -269,6 +203,79 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) } +nsresult +nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection) +{ + ConfirmSelectionInBody(); + if (action == nsEditor::kOpIgnore) return NS_OK; + + nsCOMPtrselection; + nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) + { + // dont let any txns in here move the selection around behind our back. + // Note that this won't prevent explicit selection setting from working. + nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); + + // expand the "changed doc range" as needed + res = PromoteRange(mDocChangeRange, action); + if (NS_FAILED(res)) return res; + + // add in any needed
s, and remove any unneeded ones. + res = AdjustSpecialBreaks(); + if (NS_FAILED(res)) return res; + + // merge any adjacent text nodes + if ( (action != nsEditor::kOpInsertText && + action != nsEditor::kOpInsertIMEText) ) + { + res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange); + if (NS_FAILED(res)) return res; + } + + // adjust whitespace for insert text and delete actions + if ((action == nsEditor::kOpInsertText) || + (action == nsEditor::kOpInsertIMEText) || + (action == nsEditor::kOpDeleteSelection)) + { + res = AdjustWhitespace(selection); + if (NS_FAILED(res)) return res; + } + + // replace newlines that are preformatted + // MOOSE: This is buttUgly. A better way to + // organize the action enum is in order. + if ((action == nsEditor::kOpInsertText) || + (action == nsEditor::kOpInsertIMEText) || + (action == nsHTMLEditor::kOpInsertElement) || + (action == nsHTMLEditor::kOpInsertQuotation) || + (action == nsEditor::kOpInsertNode)) + { + res = ReplaceNewlines(mDocChangeRange); + } + + // clean up any empty nodes in the selection + res = RemoveEmptyNodes(); + if (NS_FAILED(res)) return res; + + // adjust selection for insert text and delete actions + if ((action == nsEditor::kOpInsertText) || + (action == nsEditor::kOpInsertIMEText) || + (action == nsEditor::kOpDeleteSelection)) + { + res = AdjustSelection(selection, aDirection); + if (NS_FAILED(res)) return res; + } + } + + // detect empty doc + res = CreateBogusNodeIfNeeded(selection); + return res; +} + + NS_IMETHODIMP nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, @@ -303,7 +310,7 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, case kDeleteSelection: return WillDeleteSelection(aSelection, info->collapsedAction, aCancel, aHandled); case kMakeList: - return WillMakeList(aSelection, info->bOrdered, aCancel, aHandled); + return WillMakeList(aSelection, info->blockType, aCancel, aHandled); case kIndent: return WillIndent(aSelection, aCancel, aHandled); case kOutdent: @@ -314,6 +321,8 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled); case kRemoveList: return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled); + case kMakeDefListItem: + return WillMakeDefListItem(aSelection, info->blockType, aCancel, aHandled); case kInsertElement: return WillInsert(aSelection, aCancel); } @@ -349,10 +358,10 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL) PRBool bNonList = PR_FALSE; nsCOMPtr arrayOfNodes; - nsresult res = GetListActionNodes(&arrayOfNodes); + nsresult res = GetListActionNodes(&arrayOfNodes, PR_TRUE); if (NS_FAILED(res)) return res; - // yummy yummy yummy i've got nodes in my tummy + // examine list type for nodes in selection PRUint32 listCount; PRInt32 i; arrayOfNodes->Count(&listCount); @@ -389,7 +398,102 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL) NS_IMETHODIMP nsHTMLEditRules::GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent) { - return NS_ERROR_FAILURE; + aCanIndent = PR_TRUE; + aCanOutdent = PR_FALSE; + + nsCOMPtr arrayOfNodes; + nsresult res = GetListActionNodes(&arrayOfNodes, PR_TRUE); + if (NS_FAILED(res)) return res; + + // examine nodes in selection for blockquotes or list elements; + // these we can outdent. Note that we return true for canOutdent + // if *any* of the selection is outdentable, rather than all of it. + PRUint32 listCount; + PRInt32 i; + arrayOfNodes->Count(&listCount); + for (i=(PRInt32)listCount-1; i>=0; i--) + { + nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + if (nsHTMLEditUtils::IsList(curNode) || + nsHTMLEditUtils::IsListItem(curNode) || + nsHTMLEditUtils::IsBlockquote(curNode)) + { + aCanOutdent = PR_TRUE; + break; + } + } + + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::GetParagraphState(PRBool &aMixed, nsString &outFormat) +{ + // This routine is *heavily* tied to our ui choices in the paragraph + // style popup. I cant see a way around that. + aMixed = PR_TRUE; + outFormat.AssignWithConversion(""); + + PRBool bMixed = PR_FALSE; + nsAutoString formatStr; + // using "x" as anuninitialized value, since "" is meaningful + formatStr.AssignWithConversion("x"); + + nsCOMPtr arrayOfNodes; + nsresult res = GetParagraphFormatNodes(&arrayOfNodes, PR_TRUE); + if (NS_FAILED(res)) return res; + + // loop through the nodes in selection and examine their paragraph format + PRUint32 listCount; + PRInt32 i; + arrayOfNodes->Count(&listCount); + for (i=(PRInt32)listCount-1; i>=0; i--) + { + nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + nsAutoString format; + nsCOMPtr atom = mEditor->GetTag(curNode); + + if (mEditor->IsInlineNode(curNode)) + format.AssignWithConversion(""); + else if (nsIEditProperty::p == atom) + format.AssignWithConversion("P"); + else if (nsHTMLEditUtils::IsHeader(curNode)) + { + nsAutoString tag; + nsEditor::GetTagString(curNode,tag); + tag.ToUpperCase(); + format = tag; + } + else if (nsIEditProperty::blockquote == atom) + format.AssignWithConversion("BLOCKQUOTE"); + else if (nsIEditProperty::address == atom) + format.AssignWithConversion("ADDRESS"); + else if (nsIEditProperty::pre == atom) + format.AssignWithConversion("PRE"); + else if (nsIEditProperty::dt == atom) + format.AssignWithConversion("DT"); + else if (nsIEditProperty::dd == atom) + format.AssignWithConversion("DD"); + + // if this is the first node, we've found, remember it as the format + if (formatStr.EqualsWithConversion("x")) + formatStr = format; + // else make sure it matches previously found format + else if (format != formatStr) + { + bMixed = PR_TRUE; + break; + } + } + + aMixed = bMixed; + outFormat = formatStr; + return res; } @@ -825,19 +929,30 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, } } } - // is prior node inline and same type? (This shouldn't happen) - if ( mEditor->HasSameBlockNodeParent(node, priorNode) && - ( mEditor->IsInlineNode(node) || mEditor->IsTextNode(node) ) && - mEditor->NodesSameType(node, priorNode) ) + // is prior node a text node? + else if ( mEditor->IsTextNode(priorNode) ) { - // if so, join them! - nsCOMPtr topParent; - priorNode->GetParentNode(getter_AddRefs(topParent)); - *aHandled = PR_TRUE; - res = JoinNodesSmart(priorNode,node,&selNode,&selOffset); + // delete last character + nsCOMPtrnodeAsText; + nodeAsText = do_QueryInterface(priorNode); + nodeAsText->GetLength((PRUint32*)&offset); + res = aSelection->Collapse(priorNode,offset); + // just return without setting handled to true. + // default code will take care of actual deletion + return res; + } + else if ( mEditor->IsInlineNode(priorNode) ) + { + // remember where we are + res = mEditor->GetNodeLocation(priorNode, &node, &offset); if (NS_FAILED(res)) return res; + // delete it + res = mEditor->DeleteNode(priorNode); + if (NS_FAILED(res)) return res; + // we did something, so lets say so. + *aHandled = PR_TRUE; // fix up selection - res = aSelection->Collapse(selNode,selOffset); + res = aSelection->Collapse(node,offset); return res; } else return NS_OK; // punt to default @@ -924,19 +1039,30 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, } } } - // is next node inline and same type? (This shouldn't happen) - if ( mEditor->HasSameBlockNodeParent(node, nextNode) && - ( mEditor->IsInlineNode(node) || mEditor->IsTextNode(node) ) && - mEditor->NodesSameType(node, nextNode) ) + // is next node a text node? + else if ( mEditor->IsTextNode(nextNode) ) { - // if so, join them! - nsCOMPtr topParent; - nextNode->GetParentNode(getter_AddRefs(topParent)); - *aHandled = PR_TRUE; - res = JoinNodesSmart(node,nextNode,&selNode,&selOffset); + // delete last character + nsCOMPtrnodeAsText; + nodeAsText = do_QueryInterface(nextNode); + nodeAsText->GetLength((PRUint32*)&offset); + res = aSelection->Collapse(nextNode,offset); + // just return without setting handled to true. + // default code will take care of actual deletion + return res; + } + else if ( mEditor->IsInlineNode(nextNode) ) + { + // remember where we are + res = mEditor->GetNodeLocation(nextNode, &node, &offset); if (NS_FAILED(res)) return res; + // delete it + res = mEditor->DeleteNode(nextNode); + if (NS_FAILED(res)) return res; + // we did something, so lets say so. + *aHandled = PR_TRUE; // fix up selection - res = aSelection->Collapse(selNode,selOffset); + res = aSelection->Collapse(node,offset); return res; } else return NS_OK; // punt to default @@ -1001,6 +1127,11 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; if (!nodeToDelete) return NS_ERROR_NULL_POINTER; + if (mBody == nodeToDelete) + { + *aCancel = PR_TRUE; + return res; + } // if this node is text node, adjust selection if (nsEditor::IsTextNode(nodeToDelete)) @@ -1153,28 +1284,32 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsresult nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, - PRBool aOrdered, + const nsString *aListType, PRBool *aCancel, - PRBool *aHandled) + PRBool *aHandled, + const nsString *aItemType) { - if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } + if (!aSelection || !aListType || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } nsresult res = WillInsert(aSelection, aCancel); if (NS_FAILED(res)) return res; - nsAutoString listType; - listType.AssignWithConversion( aOrdered ? "ol" : "ul" ); - // initialize out param // we want to ignore result of WillInsert() *aCancel = PR_FALSE; *aHandled = PR_FALSE; - nsAutoString blockType; - blockType.AssignWithConversion( aOrdered ? "ol" : "ul"); + // deduce what tag to use for list items + nsAutoString itemType; + if (aItemType) + itemType = *aItemType; + else if (aListType->EqualsWithConversion("dl")) + itemType.AssignWithConversion("dd"); + else + itemType.AssignWithConversion("li"); PRBool outMakeEmpty; - res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty); + res = ShouldMakeEmptyBlock(aSelection, aListType, &outMakeEmpty); if (NS_FAILED(res)) return res; if (outMakeEmpty) { @@ -1186,11 +1321,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; // make sure we can put a list here - res = SplitAsNeeded(&listType, &parent, &offset); + res = SplitAsNeeded(aListType, &parent, &offset); if (NS_FAILED(res)) return res; - res = mEditor->CreateNode(listType, parent, offset, getter_AddRefs(theList)); + res = mEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList)); if (NS_FAILED(res)) return res; - res = mEditor->CreateNode(NS_ConvertASCIItoUCS2("li"), theList, 0, getter_AddRefs(theListItem)); + res = mEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem)); if (NS_FAILED(res)) return res; // put selection in new list item res = aSelection->Collapse(theListItem,0); @@ -1224,8 +1359,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, nsCOMPtr curNode( do_QueryInterface(isupports) ); while (nsHTMLEditUtils::IsDiv(curNode) - || nsHTMLEditUtils::IsOrderedList(curNode) - || nsHTMLEditUtils::IsUnorderedList(curNode) + || nsHTMLEditUtils::IsList(curNode) || nsHTMLEditUtils::IsBlockquote(curNode)) { // dive as long as there is only one child, and it is a list, div, blockquote @@ -1238,8 +1372,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, // keep diving nsCOMPtr tmpNode = nsEditor::GetChildAt(curNode, 0); if (nsHTMLEditUtils::IsDiv(tmpNode) - || nsHTMLEditUtils::IsOrderedList(tmpNode) - || nsHTMLEditUtils::IsUnorderedList(tmpNode) + || nsHTMLEditUtils::IsList(tmpNode) || nsHTMLEditUtils::IsBlockquote(tmpNode)) { // check editablility XXX floppy moose @@ -1305,37 +1438,38 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, if (nsHTMLEditUtils::IsList(curNode)) { + nsAutoString existingListStr; + res = mEditor->GetTagString(curNode, existingListStr); // do we have a curList already? if (curList && !nsHTMLEditUtils::IsDescendantOf(curNode, curList)) { - // move all of our dachildren into curList. + // move all of our children into curList. // cheezy way to do it: move whole list and then - // RemoveContainer() on the list + // RemoveContainer() on the list. + // ConvertListType first: that routine + // handles converting the list item types, if needed res = mEditor->MoveNode(curNode, curList, -1); if (NS_FAILED(res)) return res; - res = mEditor->RemoveContainer(curNode); + res = ConvertListType(curNode, &newBlock, *aListType, itemType); if (NS_FAILED(res)) return res; - } - else if ( (nsHTMLEditUtils::IsUnorderedList(curNode) && aOrdered) || - (nsHTMLEditUtils::IsOrderedList(curNode) && !aOrdered) ) - - { - // replace list with new list type - res = mEditor->ReplaceContainer(curNode,&newBlock,blockType); + res = mEditor->RemoveContainer(newBlock); if (NS_FAILED(res)) return res; - curList = newBlock; } else { - curList = curNode; + // replace list with new list type + res = ConvertListType(curNode, &newBlock, *aListType, itemType); + if (NS_FAILED(res)) return res; + curList = newBlock; } continue; } if (nsHTMLEditUtils::IsListItem(curNode)) { - if ( (nsHTMLEditUtils::IsUnorderedList(curParent) && aOrdered) || - (nsHTMLEditUtils::IsOrderedList(curParent) && !aOrdered) ) + nsAutoString existingListStr; + res = mEditor->GetTagString(curParent, existingListStr); + if ( existingListStr != *aListType ) { // list item is in wrong type of list. // if we dont have a curList, split the old list @@ -1348,16 +1482,23 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRInt32 o; res = nsEditor::GetNodeLocation(curParent, &p, &o); if (NS_FAILED(res)) return res; - res = mEditor->CreateNode(listType, p, o, getter_AddRefs(curList)); + res = mEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList)); if (NS_FAILED(res)) return res; } // move list item to new list res = mEditor->MoveNode(curNode, curList, -1); if (NS_FAILED(res)) return res; + // convert list item type if needed + if (!mEditor->NodeIsType(curNode,itemType)) + { + res = mEditor->ReplaceContainer(curNode, &newBlock, itemType); + if (NS_FAILED(res)) return res; + } } else { // item is in right type of list. But we might still have to move it. + // and we might need to convert list item types. if (!curList) curList = curParent; else @@ -1369,6 +1510,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; } } + if (!mEditor->NodeIsType(curNode,itemType)) + { + res = mEditor->ReplaceContainer(curNode, &newBlock, itemType); + if (NS_FAILED(res)) return res; + } } continue; } @@ -1377,9 +1523,9 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, // or if this node doesn't go in list we used earlier. if (!curList) // || transitionList[i]) { - res = SplitAsNeeded(&listType, &curParent, &offset); + res = SplitAsNeeded(aListType, &curParent, &offset); if (NS_FAILED(res)) return res; - res = mEditor->CreateNode(listType, curParent, offset, getter_AddRefs(curList)); + res = mEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList)); if (NS_FAILED(res)) return res; // curList is now the correct thing to put curNode in prevListItem = 0; @@ -1404,11 +1550,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, // don't wrap li around a paragraph. instead replace paragraph with li if (nsHTMLEditUtils::IsParagraph(curNode)) { - res = mEditor->ReplaceContainer(curNode, &listItem, NS_ConvertASCIItoUCS2("li")); + res = mEditor->ReplaceContainer(curNode, &listItem, itemType); } else { - res = mEditor->InsertContainerAbove(curNode, &listItem, NS_ConvertASCIItoUCS2("li")); + res = mEditor->InsertContainerAbove(curNode, &listItem, itemType); } if (NS_FAILED(res)) return res; if (nsEditor::IsInlineNode(curNode)) @@ -1525,6 +1671,18 @@ nsHTMLEditRules::WillRemoveList(nsIDOMSelection *aSelection, } +nsresult +nsHTMLEditRules::WillMakeDefListItem(nsIDOMSelection *aSelection, + const nsString *aItemType, + PRBool *aCancel, + PRBool *aHandled) +{ + // for now we let WillMakeList handle this + nsAutoString listType; + listType.AssignWithConversion("dl"); + return WillMakeList(aSelection, &listType, aCancel, aHandled, aItemType); +} + nsresult nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, @@ -1808,6 +1966,40 @@ nsHTMLEditRules::WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBoo } +/////////////////////////////////////////////////////////////////////////// +// ConvertListType: convert list type and list item type. +// +// +nsresult +nsHTMLEditRules::ConvertListType(nsIDOMNode *aList, + nsCOMPtr *outList, + const nsString& aListType, + const nsString& aItemType) +{ + if (!aList || !outList) return NS_ERROR_NULL_POINTER; + *outList = aList; // we migvht not need to change the list + nsresult res = NS_OK; + nsCOMPtr child, temp; + aList->GetFirstChild(getter_AddRefs(child)); + while (child) + { + if (!mEditor->NodeIsType(child, aItemType)) + { + res = mEditor->ReplaceContainer(child, &temp, aItemType); + if (NS_FAILED(res)) return res; + child = temp; + } + child->GetNextSibling(getter_AddRefs(temp)); + child = temp; + } + if (!mEditor->NodeIsType(aList, aListType)) + { + res = mEditor->ReplaceContainer(aList, outList, aListType); + } + return res; +} + + /////////////////////////////////////////////////////////////////////////// // CreateStyleForInsertText: take care of clearing and setting appropriate // style nodes for text insertion. @@ -1839,10 +2031,15 @@ nsHTMLEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDoc } // then process setting any styles - mEditor->mTypeInState->TakeSetProperty(&item); + PRInt32 relFontSize; - if (item) // we have at least one style to add; make a - { // new text node to insert style nodes above. + res = mEditor->mTypeInState->TakeRelativeFontSize(&relFontSize); + if (NS_FAILED(res)) return res; + res = mEditor->mTypeInState->TakeSetProperty(&item); + if (NS_FAILED(res)) return res; + + if (item || relFontSize) // we have at least one style to add; make a + { // new text node to insert style nodes above. if (mEditor->IsTextNode(node)) { // if we are in a text node, split it @@ -1863,17 +2060,29 @@ nsHTMLEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDoc node = newNode; offset = 0; weDidSometing = PR_TRUE; + + if (relFontSize) + { + PRInt32 j, dir; + // dir indicated bigger versus smaller. 1 = bigger, -1 = smaller + if (relFontSize > 0) dir=1; + else dir = -1; + for (j=0; jRelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1); + if (NS_FAILED(res)) return res; + } + } + + while (item) + { + res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value); + if (NS_FAILED(res)) return res; + // we own item now (TakeSetProperty hands ownership to us) + delete item; + mEditor->mTypeInState->TakeSetProperty(&item); + } } - - while (item) - { - res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value); - if (NS_FAILED(res)) return res; - // we own item now (TakeSetProperty hands ownership to us) - delete item; - mEditor->mTypeInState->TakeSetProperty(&item); - } - if (weDidSometing) return aSelection->Collapse(node, offset); @@ -2616,7 +2825,8 @@ nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange, nsresult nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges, nsCOMPtr *outArrayOfNodes, - PRInt32 inOperationType) + PRInt32 inOperationType, + PRBool aDontTouchContent) { if (!inArrayOfRanges || !outArrayOfNodes) return NS_ERROR_NULL_POINTER; @@ -2669,7 +2879,7 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges, { nsCOMPtr isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i)); nsCOMPtr node( do_QueryInterface(isupports) ); - if (mEditor->IsInlineNode(node) && mEditor->IsContainer(node)) + if (!aDontTouchContent && mEditor->IsInlineNode(node) && mEditor->IsContainer(node)) { nsCOMPtr arrayOfInlines; res = BustUpInlinesAtBRs(node, &arrayOfInlines); @@ -2735,7 +2945,8 @@ nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode, // GetListActionNodes: // nsresult -nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes) +nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes, + PRBool aDontTouchContent) { if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER; @@ -2748,7 +2959,58 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes) if (NS_FAILED(res)) return res; // use these ranges to contruct a list of nodes to act on. - res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList); + res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList, aDontTouchContent); + if (NS_FAILED(res)) return res; + + // pre process our list of nodes... + PRUint32 listCount; + PRInt32 i; + (*outArrayOfNodes)->Count(&listCount); + for (i=(PRInt32)listCount-1; i>=0; i--) + { + nsCOMPtr isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i)); + nsCOMPtr testNode( do_QueryInterface(isupports ) ); + + // Remove all non-editable nodes. Leave them be. + if (!mEditor->IsEditable(testNode)) + { + (*outArrayOfNodes)->RemoveElementAt(i); + } + + // scan for table elements. If we find table elements other than table, + // replace it with a list of any editable non-table content. + if (mEditor->IsTableElement(testNode) && !mEditor->IsTable(testNode)) + { + (*outArrayOfNodes)->RemoveElementAt(i); + nsCOMPtr arrayOfTableContent; + res = GetTableContent(testNode, &arrayOfTableContent); + if (NS_FAILED(res)) return res; + (*outArrayOfNodes)->AppendElements(arrayOfTableContent); + } + } + return res; +} + + +/////////////////////////////////////////////////////////////////////////// +// GetParagraphFormatNodes: +// +nsresult +nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr *outArrayOfNodes, + PRBool aDontTouchContent) +{ + if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER; + + nsCOMPtrselection; + nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + nsCOMPtr arrayOfRanges; + res = GetPromotedRanges(selection, &arrayOfRanges, kMakeList); + if (NS_FAILED(res)) return res; + + // use these ranges to contruct a list of nodes to act on. + res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeBasicBlock, aDontTouchContent); if (NS_FAILED(res)) return res; // pre process our list of nodes... @@ -3285,7 +3547,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, // Note that if _nothing_ should happen, ie, the selection is // already entireyl inside a block (or blocks) or the correct type, // then you don't want to return true in outMakeEmpty, since the - // defualt code will insert a new empty block anyway, rather than + // default code will insert a new empty block anyway, rather than // doing nothing. So we have to detect that case and return false. if (!aSelection || !outMakeEmpty) return NS_ERROR_NULL_POINTER; @@ -3342,18 +3604,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, *outMakeEmpty = PR_FALSE; return res; } - - // in an empty block? - PRBool bIsEmptyNode; - // the PR_TRUE param tell IsEmptyNode to not count moz-BRs as content - res = IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE); - if (bIsEmptyNode) - { - // we must be in a text or inline node - convert existing block - *outMakeEmpty = PR_TRUE; - return res; - } - + // is it after a
with no inline nodes after it, or a
after it?? if (offset) { @@ -3383,7 +3634,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, // we are after a
and not before inline content, // or we are between
s. // make an empty block - *outMakeEmpty = PR_FALSE; + *outMakeEmpty = PR_TRUE; return res; } } diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.h b/mozilla/editor/libeditor/html/nsHTMLEditRules.h index 1b66e084ee2..92027abc924 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.h @@ -55,6 +55,7 @@ public: // nsIHTMLEditRules methods NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL); NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent); + NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFormat); // nsIEditActionListener methods @@ -97,11 +98,12 @@ protected: nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction, PRBool *aCancel, PRBool *aHandled); - nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); + nsresult WillMakeList(nsIDOMSelection *aSelection, const nsString *aListType, PRBool *aCancel, PRBool *aHandled, const nsString *aItemType=nsnull); nsresult WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel, PRBool *aHandled); + nsresult WillMakeDefListItem(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled); nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled); nsresult DidMakeBasicBlock(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); @@ -115,17 +117,13 @@ protected: nsresult ReturnInParagraph(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled); nsresult ReturnInListItem(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset); + nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection); + nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr *outList, const nsString& aListType, const nsString& aItemType); nsresult CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDocument *aDoc); nsresult IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock, PRBool aMozBRDoesntCount = PR_FALSE, PRBool aListItemsNotEmpty = PR_FALSE); -#if 0 - nsresult IsEmptyNode(nsIDOMNode *aNode, - PRBool *outIsEmptyBlock, - PRBool aMozBRDoesntCount = PR_FALSE, - PRBool aListItemsNotEmpty = PR_FALSE); -#endif PRBool IsFirstNode(nsIDOMNode *aNode); PRBool IsLastNode(nsIDOMNode *aNode); PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock); @@ -138,11 +136,13 @@ protected: PRInt32 inOperationType); nsresult PromoteRange(nsIDOMRange *inRange, PRInt32 inOperationType); nsresult GetNodesForOperation(nsISupportsArray *inArrayOfRanges, - nsCOMPtr *outArrayOfNodes, - PRInt32 inOperationType); + nsCOMPtr *outArrayOfNodes, + PRInt32 inOperationType, + PRBool aDontTouchContent=PR_FALSE); nsresult GetChildNodesForOperation(nsIDOMNode *inNode, - nsCOMPtr *outArrayOfNodes); - nsresult GetListActionNodes(nsCOMPtr *outArrayOfNodes); + nsCOMPtr *outArrayOfNodes); + nsresult GetListActionNodes(nsCOMPtr *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE); + nsresult GetParagraphFormatNodes(nsCOMPtr *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE); nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode, nsCOMPtr *outArrayOfNodes); nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, @@ -185,7 +185,6 @@ protected: nsCOMPtr mDocChangeRange; PRBool mListenerEnabled; nsCOMPtr mUtilRange; - nsCOMPtr mBody; PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin... }; diff --git a/mozilla/editor/libeditor/html/nsHTMLEditUtils.cpp b/mozilla/editor/libeditor/html/nsHTMLEditUtils.cpp index d57310c355b..aea8d6cc470 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditUtils.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditUtils.cpp @@ -220,7 +220,9 @@ nsHTMLEditUtils::IsListItem(nsIDOMNode *node) nsAutoString tag; nsEditor::GetTagString(node,tag); tag.ToLowerCase(); - if (tag.EqualsWithConversion("li")) + if (tag.EqualsWithConversion("li") || + tag.EqualsWithConversion("dd") || + tag.EqualsWithConversion("dt")) { return PR_TRUE; } @@ -289,7 +291,8 @@ nsHTMLEditUtils::IsList(nsIDOMNode *node) nsAutoString tag; nsEditor::GetTagString(node,tag); tag.ToLowerCase(); - if ( (tag.EqualsWithConversion("ol")) || + if ( (tag.EqualsWithConversion("dl")) || + (tag.EqualsWithConversion("ol")) || (tag.EqualsWithConversion("ul")) ) { return PR_TRUE; @@ -299,7 +302,7 @@ nsHTMLEditUtils::IsList(nsIDOMNode *node) /////////////////////////////////////////////////////////////////////////// -// IsOrderedList: true if node an html orderd list +// IsOrderedList: true if node an html ordered list // PRBool nsHTMLEditUtils::IsOrderedList(nsIDOMNode *node) @@ -317,7 +320,7 @@ nsHTMLEditUtils::IsOrderedList(nsIDOMNode *node) /////////////////////////////////////////////////////////////////////////// -// IsUnorderedList: true if node an html orderd list +// IsUnorderedList: true if node an html unordered list // PRBool nsHTMLEditUtils::IsUnorderedList(nsIDOMNode *node) @@ -334,6 +337,24 @@ nsHTMLEditUtils::IsUnorderedList(nsIDOMNode *node) } +/////////////////////////////////////////////////////////////////////////// +// IsDefinitionList: true if node an html definition list +// +PRBool +nsHTMLEditUtils::IsDefinitionList(nsIDOMNode *node) +{ + NS_PRECONDITION(node, "null parent passed to nsHTMLEditUtils::IsDefinitionList"); + nsAutoString tag; + nsEditor::GetTagString(node,tag); + tag.ToLowerCase(); + if (tag.EqualsWithConversion("dl")) + { + return PR_TRUE; + } + return PR_FALSE; +} + + /////////////////////////////////////////////////////////////////////////// // IsBlockquote: true if node an html blockquote node // diff --git a/mozilla/editor/libeditor/html/nsHTMLEditUtils.h b/mozilla/editor/libeditor/html/nsHTMLEditUtils.h index e956cbf16c6..2c96510cf81 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditUtils.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditUtils.h @@ -48,8 +48,9 @@ public: static PRBool IsTableRow(nsIDOMNode *aNode); static PRBool IsTableCell(nsIDOMNode *aNode); static PRBool IsList(nsIDOMNode *aNode); - static PRBool IsUnorderedList(nsIDOMNode *aNode); static PRBool IsOrderedList(nsIDOMNode *aNode); + static PRBool IsUnorderedList(nsIDOMNode *aNode); + static PRBool IsDefinitionList(nsIDOMNode *aNode); static PRBool IsBlockquote(nsIDOMNode *aNode); static PRBool IsPre(nsIDOMNode *aNode); static PRBool IsAddress(nsIDOMNode *aNode); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index c6330c7f1cd..40a7915aa22 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -1001,6 +1001,10 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) if (!outBRNode) return NS_ERROR_NULL_POINTER; *outBRNode = nsnull; + + // calling it text insertion to trigger moz br treatment by rules + nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext); + nsresult res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; res = selection->GetIsCollapsed(&bCollapsed); @@ -1019,6 +1023,8 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) if (NS_FAILED(res)) return res; // position selection after br + res = GetNodeLocation(*outBRNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; selection->SetHint(PR_TRUE); res = selection->Collapse(selNode, selOffset+1); @@ -1127,22 +1133,28 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, if (NS_FAILED(res)) return res; // iterate range and build up array - iter->Init(range); - while (NS_ENUMERATOR_FALSE == iter->IsDone()) + res = iter->Init(range); + // init returns an error if no nodes in range. + // this can easily happen with the subtree + // iterator if the selection doesn't contain + // any *whole* nodes. + if (NS_SUCCEEDED(res)) { - res = iter->CurrentNode(getter_AddRefs(content)); - if (NS_FAILED(res)) return res; - node = do_QueryInterface(content); - if (!node) return NS_ERROR_FAILURE; - if (IsEditable(node)) - { - isupports = do_QueryInterface(node); - arrayOfNodes->AppendElement(isupports); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; } - res = iter->Next(); - if (NS_FAILED(res)) return res; } - // MOOSE: workaround for selection bug: //selection->ClearSelection(); @@ -1713,7 +1725,20 @@ PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset) NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue, - PRBool &aFirst, PRBool &aAny, PRBool &aAll) + PRBool &aFirst, + PRBool &aAny, + PRBool &aAll) +{ + return GetInlinePropertyWithAttrValue( aProperty, aAttribute, aValue, aFirst, aAny, aAll, nsnull); +} + +NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, + PRBool &aAny, + PRBool &aAll, + nsString *outValue) { if (!aProperty) return NS_ERROR_NULL_POINTER; @@ -1805,11 +1830,6 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, } return NS_OK; } - else if (aProperty == mFontAtom.get()) - { -// MOOSE! - return NS_OK; - } } // either non-collapsed selection or no cached value: do it the hard way @@ -1822,6 +1842,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, iter->Init(range); nsCOMPtr content; + nsAutoString firstValue, theValue; iter->CurrentNode(getter_AddRefs(content)); while (NS_ENUMERATOR_FALSE == iter->IsDone()) { @@ -1866,12 +1887,20 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, { PRBool isSet; nsCOMPtrresultNode; - IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode)); if (first) { + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &firstValue); aFirst = isSet; first = PR_FALSE; + if (outValue) *outValue = firstValue; } + else + { + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &theValue); + if (firstValue != theValue) + aAll = PR_FALSE; + } + if (isSet) { aAny = PR_TRUE; } @@ -2790,7 +2819,10 @@ nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat) { nsAutoString tag; tag.Assign(aParagraphFormat); tag.ToLowerCase(); - return InsertBasicBlock(tag); + if (tag.EqualsWithConversion("dd") || tag.EqualsWithConversion("dt")) + return MakeDefinitionItem(tag); + else + return InsertBasicBlock(tag); } // XXX: ERROR_HANDLING -- this method needs a little work to ensure all error codes are @@ -2907,20 +2939,52 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists) } -// get the paragraph style(s) for the selection NS_IMETHODIMP -nsHTMLEditor::GetParagraphTags(nsStringArray *aTagList) +nsHTMLEditor::GetParagraphState(PRBool &aMixed, nsString &outFormat) { -#if 0 - if (gNoisy) { printf("---------- nsHTMLEditor::GetPargraphTags ----------\n"); } -#endif - return GetParentBlockTags(aTagList, PR_FALSE); + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr htmlRules = do_QueryInterface(mRules); + if (!htmlRules) return NS_ERROR_FAILURE; + + return htmlRules->GetParagraphState(aMixed, outFormat); } NS_IMETHODIMP -nsHTMLEditor::GetListTags(nsStringArray *aTagList) +nsHTMLEditor::GetFontFaceState(PRBool &aMixed, nsString &outFace) { - return GetParentBlockTags(aTagList, PR_TRUE); + aMixed = PR_TRUE; + outFace.AssignWithConversion(""); + + nsresult res; + nsAutoString faceStr; faceStr.AssignWithConversion("face"); + PRBool first, any, all; + + res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &faceStr, nsnull, first, any, all, &outFace); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + return res; + } + + res = GetInlineProperty(nsIEditProperty::tt, nsnull, nsnull, first, any, all); + if (NS_FAILED(res)) return res; + if (any && !all) return res; // mixed + if (all) + { + aMixed = PR_FALSE; + nsIEditProperty::tt->ToString(outFace); + } + + if (!any) + { + // there was no font face attrs of any kind. We are in normal font. + outFace.AssignWithConversion(""); + aMixed = PR_FALSE; + } + return res; } NS_IMETHODIMP @@ -2934,6 +2998,17 @@ nsHTMLEditor::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL) return htmlRules->GetListState(aMixed, aOL, aUL); } +NS_IMETHODIMP +nsHTMLEditor::GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent) +{ + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr htmlRules = do_QueryInterface(mRules); + if (!htmlRules) return NS_ERROR_FAILURE; + + return htmlRules->GetIndentState(aCanIndent, aCanOutdent); +} + NS_IMETHODIMP nsHTMLEditor::MakeOrChangeList(const nsString& aListType) { @@ -2952,8 +3027,7 @@ nsHTMLEditor::MakeOrChangeList(const nsString& aListType) if (!selection) return NS_ERROR_NULL_POINTER; nsTextRulesInfo ruleInfo(nsTextEditRules::kMakeList); - if (aListType.EqualsWithConversion("ol")) ruleInfo.bOrdered = PR_TRUE; - else ruleInfo.bOrdered = PR_FALSE; + ruleInfo.blockType = &aListType; res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); if (cancel || (NS_FAILED(res))) return res; @@ -3041,8 +3115,37 @@ nsHTMLEditor::RemoveList(const nsString& aListType) return res; } +nsresult +nsHTMLEditor::MakeDefinitionItem(const nsString& aItemType) +{ + nsresult res; + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } -NS_IMETHODIMP + nsCOMPtr selection; + PRBool cancel, handled; + + nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpMakeDefListItem, nsIEditor::eNext); + + // pre-process + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + nsTextRulesInfo ruleInfo(nsTextEditRules::kMakeDefListItem); + ruleInfo.blockType = &aItemType; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (cancel || (NS_FAILED(res))) return res; + + if (!handled) + { + // todo: no default for now. we count on rules to handle it. + } + + res = mRules->DidDoAction(selection, &ruleInfo, res); + return res; +} + +nsresult nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType) { nsresult res; @@ -5534,7 +5637,8 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, const nsString *aAttribute, const nsString *aValue, PRBool &aIsSet, - nsIDOMNode **aStyleNode) const + nsIDOMNode **aStyleNode, + nsString *outValue) const { nsresult result; aIsSet = PR_FALSE; // must be initialized to false for code below to work @@ -5548,15 +5652,15 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, element = do_QueryInterface(node); if (element) { - nsAutoString tag; + nsAutoString tag, value; element->GetTagName(tag); if (propName.EqualsIgnoreCase(tag)) { PRBool found = PR_FALSE; if (aAttribute && 0!=aAttribute->Length()) { - nsAutoString value; element->GetAttribute(*aAttribute, value); + if (outValue) *outValue = value; if (value.Length()) { if (!aValue) { @@ -6182,12 +6286,14 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) res = selection->GetIsCollapsed(&bCollapsed); if (NS_FAILED(res)) return res; - // if it's collapsed dont do anything. - // MOOSE: We should probably have typing state for this like - // we do for other things. + // if it's collapsed set typing state if (bCollapsed) { - return NS_OK; + nsCOMPtr atom; + if (aSizeChange==1) atom = nsIEditProperty::big; + else atom = nsIEditProperty::small; + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + return mTypeInState->SetProp(atom, nsnull, nsnull); } // wrap with txn batching, rules sniffing, and selection preservation code @@ -6346,6 +6452,9 @@ nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, PRUint32 textLen; aTextNode->GetLength(&textLen); + // -1 is a magic value meaning to the end of node + if (aEndOffset == -1) aEndOffset = textLen; + if ( (PRUint32)aEndOffset != textLen ) { // we need to split off back of text node diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index bd9b4811f8a..da37e64dc4c 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -69,6 +69,7 @@ public: kOpAlign = 3004, kOpMakeBasicBlock = 3005, kOpRemoveList = 3006, + kOpMakeDefListItem = 3007, kOpInsertElement = 3008, kOpInsertQuotation = 3009 }; @@ -103,6 +104,11 @@ public: const nsString *aAttribute, const nsString *aValue, PRBool &aFirst, PRBool &aAny, PRBool &aAll); + NS_IMETHOD GetInlinePropertyWithAttrValue(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, PRBool &aAny, PRBool &aAll, + nsString *outValue); NS_IMETHOD RemoveAllInlineProperties(); NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute); @@ -125,13 +131,14 @@ public: NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat); NS_IMETHOD GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists); - NS_IMETHOD GetParagraphTags(nsStringArray *aTagList); - NS_IMETHOD GetListTags(nsStringArray *aTagList); + + NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFormat); + NS_IMETHOD GetFontFaceState(PRBool &aMixed, nsString &outFace); NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL); + NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent); NS_IMETHOD MakeOrChangeList(const nsString& aListType); NS_IMETHOD RemoveList(const nsString& aListType); - NS_IMETHOD InsertBasicBlock(const nsString& aBlockType); NS_IMETHOD Indent(const nsString& aIndent); NS_IMETHOD Align(const nsString& aAlign); @@ -441,7 +448,8 @@ protected: const nsString *aAttribute, const nsString *aValue, PRBool &aIsSet, - nsIDOMNode **aStyleNode) const; + nsIDOMNode **aStyleNode, + nsString *outValue = nsnull) const; /** style-based query returns PR_TRUE if (aProperty, aAttribute) is set in aSC. * WARNING: not well tested yet since we don't do style-based queries anywhere. @@ -473,6 +481,10 @@ protected: /* small utility routine to test the eEditorReadonly bit */ PRBool IsModifiable(); + /* helpers for block transformations */ + nsresult MakeDefinitionItem(const nsString& aItemType); + nsresult InsertBasicBlock(const nsString& aBlockType); + /* increase/decrease the font size of selection */ nsresult RelativeFontChange( PRInt32 aSizeChange); diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.cpp b/mozilla/editor/libeditor/text/nsTextEditRules.cpp index b782ccbdd58..18baf0ce803 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.cpp +++ b/mozilla/editor/libeditor/text/nsTextEditRules.cpp @@ -70,6 +70,9 @@ NS_NewTextEditRules(nsIEditRules** aInstancePtrResult) nsTextEditRules::nsTextEditRules() : mEditor(nsnull) , mFlags(0) // initialized to 0 ("no flags set"). Real initial value is given in Init() +, mPasswordText() +, mBogusNode(nsnull) +, mBody(nsnull) , mActionNesting(0) , mLockRulesSniffing(PR_FALSE) , mTheAction(0) @@ -106,29 +109,33 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) nsCOMPtr selection; mEditor->GetSelection(getter_AddRefs(selection)); NS_ASSERTION(selection, "editor cannot get selection"); - nsresult res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway - // create a range that is the entire body contents - if (NS_FAILED(res)) return res; + // remember our root node nsCOMPtr bodyElement; - res = mEditor->GetRootElement(getter_AddRefs(bodyElement)); + nsresult res = mEditor->GetRootElement(getter_AddRefs(bodyElement)); if (NS_FAILED(res)) return res; if (!bodyElement) return NS_ERROR_NULL_POINTER; - nsCOMPtrbodyNode = do_QueryInterface(bodyElement); - if (!bodyNode) return NS_ERROR_FAILURE; + mBody = do_QueryInterface(bodyElement); + if (!mBody) return NS_ERROR_FAILURE; + + // put in a magic br if needed + res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway + if (NS_FAILED(res)) return res; + + // create a range that is the entire body contents nsCOMPtr wholeDoc; res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), getter_AddRefs(wholeDoc)); if (NS_FAILED(res)) return res; - wholeDoc->SetStart(bodyNode,0); + wholeDoc->SetStart(mBody,0); nsCOMPtr list; - res = bodyNode->GetChildNodes(getter_AddRefs(list)); + res = mBody->GetChildNodes(getter_AddRefs(list)); if (NS_FAILED(res) || !list) return res?res:NS_ERROR_FAILURE; PRUint32 listCount; res = list->GetLength(&listCount); if (NS_FAILED(res)) return res; - res = wholeDoc->SetEnd(bodyNode,listCount); + res = wholeDoc->SetEnd(mBody,listCount); if (NS_FAILED(res)) return res; // replace newlines in that range with breaks @@ -1188,19 +1195,14 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection) // tell rules system to not do any post-processing nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone); - nsCOMPtr bodyElement; - - nsresult res = mEditor->GetRootElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(res)) return res; - if (!bodyElement) return NS_ERROR_NULL_POINTER; - nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + if (!mBody) return NS_ERROR_NULL_POINTER; // now we've got the body tag. // iterate the body tag, looking for editable content // if no editable content is found, insert the bogus node PRBool needsBogusContent=PR_TRUE; nsCOMPtrbodyChild; - res = bodyNode->GetFirstChild(getter_AddRefs(bodyChild)); + nsresult res = mBody->GetFirstChild(getter_AddRefs(bodyChild)); while ((NS_SUCCEEDED(res)) && bodyChild) { if (mEditor->IsMozEditorBogusNode(bodyChild) || mEditor->IsEditable(bodyChild)) @@ -1214,25 +1216,27 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection) } if (needsBogusContent) { - // set mBogusNode to be the newly created
- res = mEditor->CreateNode(NS_ConvertASCIItoUCS2("br"), bodyNode, 0, - getter_AddRefs(mBogusNode)); + // create a br + nsCOMPtr domDoc; + res = mEditor->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtrbrElement; + res = domDoc->CreateElement(NS_ConvertASCIItoUCS2("br"),getter_AddRefs(brElement)); if (NS_FAILED(res)) return res; + + // set mBogusNode to be the newly created
+ mBogusNode = do_QueryInterface(brElement); if (!mBogusNode) return NS_ERROR_NULL_POINTER; // give it a special attribute - nsCOMPtrnewPElement; - newPElement = do_QueryInterface(mBogusNode); - if (newPElement) - { - newPElement->SetAttribute( - NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeAttr), - NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeValue) - ); - } + brElement->SetAttribute( NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeAttr), + NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeValue) ); + // put the node in the document + res = mEditor->InsertNode(mBogusNode,mBody,0); + if (NS_FAILED(res)) return res; + // set selection - aSelection->Collapse(bodyNode,0); + aSelection->Collapse(mBody,0); } return res; } diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.h b/mozilla/editor/libeditor/text/nsTextEditRules.h index b241f9c3970..9f1329be9a3 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.h +++ b/mozilla/editor/libeditor/text/nsTextEditRules.h @@ -81,6 +81,7 @@ public: kAlign = 3004, kMakeBasicBlock = 3005, kRemoveList = 3006, + kMakeDefListItem = 3007, kInsertElement = 3008 }; @@ -173,6 +174,7 @@ protected: nsHTMLEditor *mEditor; // note that we do not refcount the editor nsString mPasswordText; // a buffer we use to store the real value of password editors nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc + nsCOMPtr mBody; // cached root node PRUint32 mFlags; PRUint32 mActionNesting; PRBool mLockRulesSniffing; @@ -195,7 +197,6 @@ class nsTextRulesInfo : public nsRulesInfo outputFormat(0), maxLength(-1), collapsedAction(nsIEditor::eNext), - bOrdered(PR_FALSE), alignType(0), blockType(0), insertElement(0) diff --git a/mozilla/editor/public/nsIHTMLEditor.h b/mozilla/editor/public/nsIHTMLEditor.h index 2fc0b4b76af..402ff7be3b3 100644 --- a/mozilla/editor/public/nsIHTMLEditor.h +++ b/mozilla/editor/public/nsIHTMLEditor.h @@ -121,6 +121,12 @@ public: const nsString *aValue, PRBool &aFirst, PRBool &aAny, PRBool &aAll)=0; + NS_IMETHOD GetInlinePropertyWithAttrValue(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, PRBool &aAny, PRBool &aAll, + nsString *outValue)=0; + /** * RemoveAllInlineProperties() deletes all the inline properties from all * text in the current selection. @@ -237,16 +243,6 @@ public: */ NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode)=0; - /** - * GetListState returns what list type is in the selection. - * @param aMixed True if there is more than one type of list, or - * if there is some list and non-list - * @param aOL The company that employs me. No, really, it's - * true if an "ol" list is selected. - * @param aUL true if an "ul" list is selected. - */ - NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)=0; - /* ------------ Selection manipulation -------------- */ /* Should these be moved to nsIDOMSelection? */ @@ -281,19 +277,34 @@ public: NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat)=0; /** - * Get a list of tagnames of all paragraph tags - * (the closest block parent tags) of all - * elements in the current selection + * Document me! * */ - NS_IMETHOD GetParagraphTags(nsStringArray *aTagList)=0; + NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFace)=0; - /** - * Get a list of tagnames of all list tags - * (the closest parent tags that are UL, OL, or DL) of all - * elements in the current selection + /** + * GetFontFaceState returns what font face is in the selection. + * @param aMixed True if there is more than one font face + * @param outFace name of face. Note: "tt" is returned for + * tt tag. "" is returned for none. */ - NS_IMETHOD GetListTags(nsStringArray *aTagList)=0; + NS_IMETHOD GetFontFaceState(PRBool &aMixed, nsString &outFont)=0; + + /** + * GetListState returns what list type is in the selection. + * @param aMixed True if there is more than one type of list, or + * if there is some list and non-list + * @param aOL The company that employs me. No, really, it's + * true if an "ol" list is selected. + * @param aUL true if an "ul" list is selected. + */ + NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)=0; + + /** + * Document me! + * + */ + NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent)=0; /** * Document me! diff --git a/mozilla/editor/ui/composer/content/editor.js b/mozilla/editor/ui/composer/content/editor.js index 80c291221a0..4d0e7fee2b5 100644 --- a/mozilla/editor/ui/composer/content/editor.js +++ b/mozilla/editor/ui/composer/content/editor.js @@ -380,6 +380,7 @@ function EditorSaveDocument(doSaveAs, doSaveCopy) function EditorCanClose() { // Returns FALSE only if user cancels save action + dump("Calling EditorCanClose\n"); return editorShell.CheckAndSaveDocument(GetString("BeforeClosing")); } @@ -434,65 +435,94 @@ function EditorSetTextProperty(property, attribute, value) gContentWindow.focus(); } -function onParagraphFormatChange(commandID) +function onParagraphFormatChange(paraMenuList, commandID) { - var commandNode = document.getElementById(commmandID); + var commandNode = document.getElementById(commandID); var state = commandNode.getAttribute("state"); -// dump(" ==== onParagraphFormatChange was called. state="+state+"|\n"); - return; //TODO: REWRITE THIS + dump("Updating font face with " + state + "\n"); + + // force match with "normal" + if (state == "body") + state = ""; + + if (state == "mixed") + { + //Selection is the "mixed" ( > 1 style) state + paraMenuList.selectedItem = null; + paraMenuList.setAttribute("value",GetString('MixedFormats')); + } + else + { + var menuPopup = document.getElementById("ParagraphPopup"); + var menuItems = menuPopup.childNodes; + for (i=0; i < menuItems.length; i++) + { + var menuItem = menuItems.item(i); + if (menuItem.data == state) + { + paraMenuList.selectedItem = menuItem; + break; + } + } + } } function EditorSetParagraphFormat(commandID, paraFormat) { - // editorShell.SetParagraphFormat(paraFormat); var commandNode = document.getElementById(commandID); - dump("Saving para format state " + paraFormat + "\n"); commandNode.setAttribute("state", paraFormat); window.content.focus(); // needed for command dispatch to work goDoCommand(commandID); } -function onFontFaceChange() +function onFontFaceChange(fontFaceMenuList, commandID) { - return; //TODO: REWRITE THIS - var select = document.getElementById("FontFaceSelect"); - if (select) - { - // Default selects "Variable Width" - var newIndex = 0; - var face = select.getAttribute("face"); -//dump("onFontFaceChange: face="+face+"\n"); - if ( face == "mixed") + var commandNode = document.getElementById(commandID); + var state = commandNode.getAttribute("state"); + + dump("Updating font face with " + state + "\n"); + + if (state == "mixed") + { + //Selection is the "mixed" ( > 1 style) state + fontFaceMenuList.selectedItem = null; + fontFaceMenuList.setAttribute("value",GetString('MixedFormats')); + } + else + { + var menuPopup = document.getElementById("FontFacePopup"); + var menuItems = menuPopup.childNodes; + for (i=0; i < menuItems.length; i++) { - // No single type selected - newIndex = -1; - } - else - { - for( var i = 0; i < gFontFaceNames.length; i++) + var menuItem = menuItems.item(i); + if (menuItem.getAttribute("value") && (menuItem.data.toLowerCase() == state.toLowerCase())) { - if( gFontFaceNames[i] == face ) - { - newIndex = i; - break; - } + fontFaceMenuList.selectedItem = menuItem; + break; } } - if (select.selectedIndex != newIndex) - select.selectedIndex = newIndex; } } -function EditorSetFontFace(fontFace) +function EditorSetFontFace(commandID, fontFace) { - if (fontFace == "tt") { + dump("Setting font face to " + fontFace + "\n"); + var commandNode = document.getElementById(commandID); + commandNode.setAttribute("state", fontFace); + window.content.focus(); // needed for command dispatch to work + goDoCommand(commandID); +/* + if (fontFace == "tt") + { // The old "teletype" attribute editorShell.SetTextProperty("tt", "", ""); // Clear existing font face editorShell.RemoveTextProperty("font", "face"); - } else { + } + else + { // Remove any existing TT nodes editorShell.RemoveTextProperty("tt", "", ""); @@ -502,8 +532,8 @@ function EditorSetFontFace(fontFace) editorShell.SetTextProperty("font", "face", fontFace); } } -//dump("Setting focus to content window...\n"); - gContentWindow.focus(); + window.content.focus(); +*/ } function EditorSelectFontSize() @@ -519,33 +549,29 @@ function EditorSelectFontSize() } } -function onFontSizeChange() +function onFontSizeChange(fontSizeMenulist, commandID) { - var select = document.getElementById("FontFaceSelect"); - if (select) - { - // If we don't match anything, set to "0 (normal)" - var newIndex = 2; - var size = select.getAttribute("size"); - if ( size == "mixed") + // If we don't match anything, set to "0 (normal)" + var newIndex = 2; + var size = fontSizeMenulist.getAttribute("size"); + if ( size == "mixed") + { + // No single type selected + newIndex = -1; + } + else + { + for( var i = 0; i < gFontSizeNames.length; i++) { - // No single type selected - newIndex = -1; - } - else - { - for( var i = 0; i < gFontSizeNames.length; i++) + if( gFontSizeNames[i] == size ) { - if( gFontSizeNames[i] == size ) - { - newIndex = i; - break; - } + newIndex = i; + break; } } - if (select.selectedIndex != newIndex) - select.selectedIndex = newIndex; } + if (fontSizeMenulist.selectedIndex != newIndex) + fontSizeMenulist.selectedIndex = newIndex; } function EditorSetFontSize(size) @@ -553,7 +579,7 @@ function EditorSetFontSize(size) if( size == "0" || size == "normal" || size == "medium" ) { - editorShell.RemoveTextProperty("font", size); + editorShell.RemoveTextProperty("font", "size"); dump("Removing font size\n"); } else { dump("Setting font size\n"); @@ -641,7 +667,7 @@ dump("EditorSelectBackColor: "+color+"\n"); if (menupopup) menupopup.closePopup(); EditorSetBackgroundColor(color); - gContentWindow.focus(); + window.content.focus(); } function EditorRemoveBackColor(ColorWellID) @@ -653,7 +679,7 @@ function EditorRemoveBackColor(ColorWellID) } //TODO: Set colorwell to browser's default color editorShell.SetBackgroundColor(""); - gContentWindow.focus(); + window.content.focus(); } @@ -668,26 +694,25 @@ function SetManualTextColor() function EditorSetFontColor(color) { editorShell.SetTextProperty("font", "color", color); - gContentWindow.focus(); + window.content.focus(); } function EditorSetBackgroundColor(color) { editorShell.SetBackgroundColor(color); - gContentWindow.focus(); + window.content.focus(); } function EditorApplyStyle(tagName) { - dump("applying style\n"); editorShell.SetTextProperty(tagName, "", ""); - gContentWindow.focus(); + window.content.focus(); } function EditorRemoveLinks() { editorShell.RemoveTextProperty("href", ""); - gContentWindow.focus(); + window.content.focus(); } /*TODO: We need an oncreate hook to do enabling/disabling for the @@ -771,8 +796,9 @@ function SetEditMode(mode) if (bodyNode) editorShell.editorSelection.collapse(bodyNode, 0); - gContentWindow.focus(); - setTimeout("gContentWindow.focus()", 10); + window.content.focus(); + // yuck. what is this? + setTimeout("window.content.focus()", 10); } } } @@ -828,7 +854,7 @@ function SetDisplayMode(mode) // TODO: WE MUST ENABLE APPROPRIATE COMMANDS // and change UI back to "normal" - gContentWindow.focus(); + window.content.focus(); } return true; } @@ -919,6 +945,9 @@ function EditorInitEditMenu() { var DelStr = GetString(gIsMac ? "Clear" : "Delete"); + // Yuck. We should be doing this at build time, using + // platform-specific overlays or dtd files. + // Change menu text to "Clear" for Mac // TODO: Should this be in globalOverlay.j? document.getElementById("menu_delete").setAttribute("value",DelStr); diff --git a/mozilla/editor/ui/composer/content/editorOverlay.xul b/mozilla/editor/ui/composer/content/editorOverlay.xul index 7fa72bd5f2d..e3e8191de0e 100644 --- a/mozilla/editor/ui/composer/content/editorOverlay.xul +++ b/mozilla/editor/ui/composer/content/editorOverlay.xul @@ -182,6 +182,8 @@ + + @@ -234,7 +236,6 @@ - @@ -430,7 +431,7 @@ - + @@ -558,7 +559,7 @@ - + @@ -573,7 +574,7 @@ - + @@ -771,8 +772,8 @@ - - + + @@ -792,8 +793,8 @@ - - + + @@ -804,7 +805,7 @@ - + diff --git a/mozilla/editor/ui/composer/locale/en-US/editor.properties b/mozilla/editor/ui/composer/locale/en-US/editor.properties index 990280625ff..60a007fc3fd 100644 --- a/mozilla/editor/ui/composer/locale/en-US/editor.properties +++ b/mozilla/editor/ui/composer/locale/en-US/editor.properties @@ -60,6 +60,8 @@ EmptyHREFError=You must enter or choose a location (URL) to create a new link. LinkText=Link text: LinkImage=Link image: MixedSelection=[Mixed selection] +MixedFormats=(mixed) +MixedFonts=(mixed) EnterLinkText=Enter text to display for the link: EmptyLinkTextError=You must enter some text for this link. #Don't translate: %n% %min% %max% @@ -115,3 +117,4 @@ Clear=Clear #Mouse actions Click=Click Drag=Drag +Unknown=Unknown \ No newline at end of file