/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * */ #include "nsIEditor.h" #include "nsIHTMLEditor.h" #include "nsIEditorShell.h" #include "nsIDOMSelection.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" #include "nsIDOMWindow.h" #include "nsIDOMDocument.h" #include "nsIClipboard.h" #include "nsXPIDLString.h" #include "nsComposerCommands.h" // utility methods //--------------------------------------------------------------------------- static PRBool XULNodeExists(nsIEditorShell* aEditorShell, const char* nodeID) //--------------------------------------------------------------------------- { nsCOMPtr webshellWindow; aEditorShell->GetWebShellWindow(getter_AddRefs(webshellWindow)); nsCOMPtr chromeDoc; webshellWindow->GetDocument(getter_AddRefs(chromeDoc)); if (!chromeDoc) return PR_FALSE; nsCOMPtr elem; nsresult rv = chromeDoc->GetElementById( NS_ConvertASCIItoUCS2(nodeID), getter_AddRefs(elem) ); return NS_SUCCEEDED(rv) && (elem.get() != nsnull); } //--------------------------------------------------------------------------- static nsresult SetNodeAttribute(nsIEditorShell* aEditorShell, const PRUnichar* nodeID, const PRUnichar* attributeName, const nsString& newValue) //--------------------------------------------------------------------------- { nsCOMPtr webshellWindow; aEditorShell->GetWebShellWindow(getter_AddRefs(webshellWindow)); nsCOMPtr chromeDoc; webshellWindow->GetDocument(getter_AddRefs(chromeDoc)); if (!chromeDoc) return PR_FALSE; nsCOMPtr elem; nsresult rv = chromeDoc->GetElementById( nsAutoString(nodeID), getter_AddRefs(elem) ); if (NS_FAILED(rv) || !elem) return rv; return elem->SetAttribute(nsAutoString(attributeName), newValue); } //--------------------------------------------------------------------------- static nsresult GetNodeAttribute(nsIEditorShell* aEditorShell, const PRUnichar* nodeID, const PRUnichar* attributeName, nsString& outValue) //--------------------------------------------------------------------------- { nsCOMPtr webshellWindow; aEditorShell->GetWebShellWindow(getter_AddRefs(webshellWindow)); nsCOMPtr chromeDoc; webshellWindow->GetDocument(getter_AddRefs(chromeDoc)); if (!chromeDoc) return PR_FALSE; nsCOMPtr elem; nsresult rv = chromeDoc->GetElementById( nsAutoString(nodeID), getter_AddRefs(elem) ); if (NS_FAILED(rv) || !elem) return rv; return elem->GetAttribute(nsAutoString(attributeName), outValue); } #ifdef XP_MAC #pragma mark - #endif nsBaseStateUpdatingCommand::nsBaseStateUpdatingCommand(const char* aTagName) : nsBaseCommand() , mTagName(aTagName) { } nsBaseStateUpdatingCommand::~nsBaseStateUpdatingCommand() { } NS_IMPL_ISUPPORTS_INHERITED1(nsBaseStateUpdatingCommand, nsBaseCommand, nsIStateUpdatingControllerCommand); NS_IMETHODIMP nsBaseStateUpdatingCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (!editorShell) return NS_OK; // check for plain text? nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (!editor) return NS_OK; *outCmdEnabled = PR_TRUE; // also udpate the command state return UpdateCommandState(aCommand, refCon); } NS_IMETHODIMP nsBaseStateUpdatingCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); if (!editorShell) return NS_ERROR_NOT_INITIALIZED; nsresult rv = ToggleState(editorShell, mTagName); if (NS_FAILED(rv)) return rv; return UpdateCommandState(aCommand, refCon); } NS_IMETHODIMP nsBaseStateUpdatingCommand::UpdateCommandState(const PRUnichar *aCommandName, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { PRBool stateIsSet; rv = GetCurrentState(editorShell, mTagName, stateIsSet); if (NS_FAILED(rv)) return rv; PRBool oldState = !stateIsSet; // what was it last set to? void* stateData; if (NS_SUCCEEDED(editorShell->GetCommandStateData(aCommandName, &stateData))) oldState = (PRBool)stateData; if (stateIsSet != oldState) { // poke the UI rv = SetNodeAttribute(editorShell, aCommandName, NS_ConvertASCIItoUCS2("state").GetUnicode(), NS_ConvertASCIItoUCS2(stateIsSet ? "true" : "false")); editorShell->SetCommandStateData(aCommandName, (void*)stateIsSet); } } return rv; } #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 NS_IMETHODIMP nsCloseCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { *outCmdEnabled = PR_TRUE; // check if loading etc? } return NS_OK; } NS_IMETHODIMP nsCloseCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { PRBool wasConfirmed; rv = editorShell->CloseWindow(&wasConfirmed); } return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsPrintingCommands::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (!editorShell) return NS_OK; nsAutoString cmdString(aCommand); if (cmdString.EqualsWithConversion("cmd_print")) *outCmdEnabled = PR_TRUE; else if (cmdString.EqualsWithConversion("cmd_printSetup")) *outCmdEnabled = PR_FALSE; // not implemented yet else if (cmdString.EqualsWithConversion("cmd_printPreview")) *outCmdEnabled = PR_FALSE; // not implemented yet return NS_OK; } NS_IMETHODIMP nsPrintingCommands::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_print")) rv = editorShell->Print(); else if (cmdString.EqualsWithConversion("cmd_printSetup")) rv = NS_ERROR_NOT_IMPLEMENTED; else if (cmdString.EqualsWithConversion("cmd_printPreview")) rv = NS_ERROR_NOT_IMPLEMENTED; return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsSaveCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { PRBool docModified; if (NS_SUCCEEDED(editorShell->GetDocumentModified(&docModified))) *outCmdEnabled = docModified; } return NS_OK; } NS_IMETHODIMP nsSaveCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { PRBool wasSaved; rv = editorShell->SaveDocument(PR_FALSE, PR_FALSE, &wasSaved); } return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsSaveAsCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = (editorShell.get() != nsnull); return NS_OK; } NS_IMETHODIMP nsSaveAsCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { PRBool wasSaved; rv = editorShell->SaveDocument(PR_TRUE, PR_FALSE, &wasSaved); } return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsPasteQuotationCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (editor) editor->CanPaste(nsIClipboard::kGlobalClipboard, *outCmdEnabled); } return NS_OK; } NS_IMETHODIMP nsPasteQuotationCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { rv = editorShell->PasteAsQuotation(nsIClipboard::kGlobalClipboard); } return rv; } #ifdef XP_MAC #pragma mark - #endif nsStyleUpdatingCommand::nsStyleUpdatingCommand(const char* aTagName) : nsBaseStateUpdatingCommand(aTagName) { } nsresult nsStyleUpdatingCommand::GetCurrentState(nsIEditorShell *aEditorShell, const char* aTagName, PRBool& outStyleSet) { NS_ASSERTION(aEditorShell, "Need editor shell here"); nsresult rv = NS_OK; PRBool firstOfSelectionHasProp = PR_FALSE; PRBool anyOfSelectionHasProp = PR_FALSE; PRBool allOfSelectionHasProp = PR_FALSE; nsCOMPtr editor; aEditorShell->GetEditor(getter_AddRefs(editor)); nsCOMPtr htmlEditor = do_QueryInterface(editor); if (!htmlEditor) return NS_ERROR_NOT_INITIALIZED; nsCOMPtr styleAtom = getter_AddRefs(NS_NewAtom(aTagName)); rv = htmlEditor->GetInlineProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); outStyleSet = allOfSelectionHasProp; // change this to alter the behaviour return rv; } nsresult nsStyleUpdatingCommand::ToggleState(nsIEditorShell *aEditorShell, const char* aTagName) { PRBool styleSet; nsresult rv = GetCurrentState(aEditorShell, aTagName, styleSet); if (NS_FAILED(rv)) return rv; nsAutoString tagName; tagName.AssignWithConversion(aTagName); if (styleSet) rv = aEditorShell->RemoveTextProperty(tagName.GetUnicode(), nsnull); else rv = aEditorShell->SetTextProperty(tagName.GetUnicode(), nsnull, nsnull); return rv; } #ifdef XP_MAC #pragma mark - #endif nsListCommand::nsListCommand(const char* aTagName) : nsBaseStateUpdatingCommand(aTagName) { } nsresult nsListCommand::GetCurrentState(nsIEditorShell *aEditorShell, const char* aTagName, PRBool& outInList) { NS_ASSERTION(aEditorShell, "Need editorShell here"); nsCOMPtr editor; aEditorShell->GetEditor(getter_AddRefs(editor)); if (!editor) return NS_ERROR_UNEXPECTED; nsCOMPtr domSelection; editor->GetSelection(getter_AddRefs(domSelection)); if (!domSelection) return NS_ERROR_UNEXPECTED; nsCOMPtr domNode; domSelection->GetAnchorNode(getter_AddRefs(domNode)); if (!domNode) return NS_ERROR_UNEXPECTED; // tagStr will hold the list state when we're done. nsAutoString tagStr; tagStr.AssignWithConversion("ol"); nsCOMPtr parentElement; nsresult rv = aEditorShell->GetElementOrParentByTagName(tagStr.GetUnicode(), domNode, getter_AddRefs(parentElement)); if (NS_FAILED(rv)) return rv; if (!parentElement) { tagStr.AssignWithConversion("ul"); rv = aEditorShell->GetElementOrParentByTagName(tagStr.GetUnicode(), domNode, getter_AddRefs(parentElement)); if (NS_FAILED(rv)) return rv; if (!parentElement) tagStr.Truncate(); } outInList = tagStr.EqualsWithConversion(mTagName); return NS_OK; } nsresult nsListCommand::ToggleState(nsIEditorShell *aEditorShell, const char* aTagName) { PRBool inList; nsresult rv = GetCurrentState(aEditorShell, aTagName, inList); if (NS_FAILED(rv)) return rv; nsAutoString listType; listType.AssignWithConversion(aTagName); if (inList) rv = aEditorShell->RemoveList(listType.GetUnicode()); else rv = aEditorShell->MakeOrChangeList(listType.GetUnicode()); return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsIndentCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (editor) *outCmdEnabled = PR_TRUE; // can always indent (I guess) } return NS_OK; } NS_IMETHODIMP nsIndentCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { nsAutoString indentStr; indentStr.AssignWithConversion("indent"); rv = editorShell->Indent(indentStr.GetUnicode()); } return rv; } NS_IMETHODIMP nsOutdentCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (editor) { // XXX fix me. You can't outdent if you're already at the top level. //PRBool canOutdent; //editor->CanIndent("outdent", &canOutdent); *outCmdEnabled = PR_TRUE; } } return NS_OK; } NS_IMETHODIMP nsOutdentCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { nsAutoString indentStr; indentStr.AssignWithConversion("outdent"); rv = editorShell->Indent(indentStr.GetUnicode()); } return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (editor) { *outCmdEnabled = PR_TRUE; } } return NS_OK; } NS_IMETHODIMP nsParagraphStateCommand::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 = GetNodeAttribute(editorShell, aCommand, NS_ConvertASCIItoUCS2("state").GetUnicode(), stateAttribute); if (NS_FAILED(rv)) return rv; rv = editorShell->SetParagraphFormat(stateAttribute.GetUnicode()); } return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsAlignCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (editor) { *outCmdEnabled = PR_TRUE; } } return NS_OK; } NS_IMETHODIMP nsAlignCommand::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 = GetNodeAttribute(editorShell, aCommand, NS_ConvertASCIItoUCS2("state").GetUnicode(), stateAttribute); if (NS_FAILED(rv)) return rv; rv = editorShell->Align(stateAttribute.GetUnicode()); } return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsRemoveStylesCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (editor) { // test if we have any styles? *outCmdEnabled = PR_TRUE; } } return NS_OK; } NS_IMETHODIMP nsRemoveStylesCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { rv = editorShell->RemoveTextProperty(NS_ConvertASCIItoUCS2("all").GetUnicode(), NS_ConvertASCIItoUCS2("").GetUnicode()); } return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsIncreaseFontSizeCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (editor) { // test if we are at the max size? *outCmdEnabled = PR_TRUE; } } return NS_OK; } NS_IMETHODIMP nsIncreaseFontSizeCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { rv = editorShell->IncreaseFontSize(); } return rv; } #ifdef XP_MAC #pragma mark - #endif NS_IMETHODIMP nsDecreaseFontSizeCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled) { nsCOMPtr editorShell = do_QueryInterface(refCon); *outCmdEnabled = PR_FALSE; if (editorShell) { nsCOMPtr editor; editorShell->GetEditor(getter_AddRefs(editor)); if (editor) { // test if we are at the min size? *outCmdEnabled = PR_TRUE; } } return NS_OK; } NS_IMETHODIMP nsDecreaseFontSizeCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon) { nsCOMPtr editorShell = do_QueryInterface(refCon); nsresult rv = NS_OK; if (editorShell) { rv = editorShell->DecreaseFontSize(); } return rv; }