diff --git a/editor/base/CreateElementTxn.cpp b/editor/base/CreateElementTxn.cpp index fa7156334fda..930a59ec7599 100644 --- a/editor/base/CreateElementTxn.cpp +++ b/editor/base/CreateElementTxn.cpp @@ -79,7 +79,7 @@ NS_IMETHODIMP CreateElementTxn::Do(void) result = mEditor->GetDocument(getter_AddRefs(doc)); if ((NS_SUCCEEDED(result)) && (doc)) { - if (nsIEditor::GetTextNodeTag() == mTag) + if (nsEditor::GetTextNodeTag() == mTag) { const nsString stringData; nsCOMPtrnewTextNode; diff --git a/editor/base/DeleteRangeTxn.cpp b/editor/base/DeleteRangeTxn.cpp index 33319eed5f74..79860e0a795d 100644 --- a/editor/base/DeleteRangeTxn.cpp +++ b/editor/base/DeleteRangeTxn.cpp @@ -123,7 +123,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) else { // the selection ends in a different node from where it started // delete the relevant content in the start node - result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eDeleteRight); + result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eDeleteNext); if (NS_SUCCEEDED(result)) { // delete the intervening nodes @@ -131,7 +131,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) if (NS_SUCCEEDED(result)) { // delete the relevant content in the end node - result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eDeleteLeft); + result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eDeletePrevious); } } } @@ -281,7 +281,7 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, PRUint32 aOffset, - nsIEditor::ECollapsedSelectionAction aAction) + nsIEditor::ESelectionCollapseDirection aAction) { nsresult result; // see what kind of node we have @@ -290,7 +290,7 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, if (textNode) { // if the node is a text node, then delete text content PRUint32 start, numToDelete; - if (nsIEditor::eDeleteRight == aAction) + if (nsIEditor::eDeleteNext == aAction) { start=aOffset; textNode->GetLength(&numToDelete); diff --git a/editor/base/DeleteRangeTxn.h b/editor/base/DeleteRangeTxn.h index 9df23001fe7e..0d1fe5af79f7 100644 --- a/editor/base/DeleteRangeTxn.h +++ b/editor/base/DeleteRangeTxn.h @@ -32,6 +32,9 @@ class nsIDOMDocument; 0x5ec6b260, 0xac49, 0x11d2, \ {0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } +class nsIDOMRange; +class nsIEditor; + /** * A transaction that deletes an entire range in the content tree */ @@ -78,7 +81,7 @@ protected: NS_IMETHOD CreateTxnsToDeleteContent(nsIDOMNode *aParent, PRUint32 aOffset, - nsIEditor::ECollapsedSelectionAction aAction); + nsIEditor::ESelectionCollapseDirection aAction); protected: diff --git a/editor/base/EditTable.cpp b/editor/base/EditTable.cpp index 8903627af307..c28e0c4df658 100644 --- a/editor/base/EditTable.cpp +++ b/editor/base/EditTable.cpp @@ -36,6 +36,8 @@ #include "nsITableLayout.h" // data owned by the table and cell frames #include "nsHTMLEditor.h" +#include "nsEditorUtils.h" + static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); // Table Editing methods @@ -244,7 +246,7 @@ nsHTMLEditor::GetCellIndexes(nsIDOMElement *aCell, PRInt32 &aColIndex, PRInt32 & res = NS_ERROR_FAILURE; // we return an error unless we get the index nsISupports *layoutObject=nsnull; // frames are not ref counted, so don't use an nsCOMPtr - res = nsEditor::GetLayoutObject(aCell, &layoutObject); + res = nsHTMLEditor::GetLayoutObject(aCell, &layoutObject); if ((NS_SUCCEEDED(res)) && (nsnull!=layoutObject)) { // get the table cell interface from the frame @@ -268,7 +270,7 @@ nsHTMLEditor::GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **table // frames are not ref counted, so don't use an nsCOMPtr nsISupports *layoutObject=nsnull; - nsresult res = nsEditor::GetLayoutObject(aTable, &layoutObject); + nsresult res = nsHTMLEditor::GetLayoutObject(aTable, &layoutObject); if ((NS_SUCCEEDED(res)) && (nsnull!=layoutObject)) { // get the table interface from the frame diff --git a/editor/base/Makefile.in b/editor/base/Makefile.in index 8e8dc8adbc1d..6f448b6700f4 100644 --- a/editor/base/Makefile.in +++ b/editor/base/Makefile.in @@ -27,16 +27,15 @@ IS_COMPONENT = 1 CPPSRCS = \ nsEditor.cpp \ - nsTextEditor.cpp \ + nsEditorUtils.cpp \ + nsEditorRegistration.cpp \ nsTextEditRules.cpp \ TextEditorTest.cpp \ nsHTMLEditRules.cpp \ nsEditorEventListeners.cpp \ nsEditProperty.cpp \ - nsEditFactory.cpp \ - nsHTMLEditFactory.cpp \ + nsEditorFactory.cpp \ nsHTMLEditor.cpp \ - nsTextEditFactory.cpp \ ChangeAttributeTxn.cpp \ EditTxn.cpp \ EditAggregateTxn.cpp \ @@ -53,6 +52,7 @@ CPPSRCS = \ JoinElementTxn.cpp \ nsStyleSheetTxns.cpp \ TransactionFactory.cpp \ + TypeInState.cpp \ nsInternetCiter.cpp \ nsAOLCiter.cpp \ nsInterfaceState.cpp \ diff --git a/editor/base/TextEditorTest.cpp b/editor/base/TextEditorTest.cpp index c234063a0ae1..91f89e6cb563 100644 --- a/editor/base/TextEditorTest.cpp +++ b/editor/base/TextEditorTest.cpp @@ -17,7 +17,7 @@ */ #include -#include "nsITextEditor.h" + #include "nsIEditor.h" #include "TextEditorTest.h" #include "nsIDOMSelection.h" @@ -43,7 +43,7 @@ TextEditorTest::~TextEditorTest() printf("destroyed a TextEditorTest\n"); } -void TextEditorTest::Run(nsITextEditor *aEditor, PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) +void TextEditorTest::Run(nsIEditor *aEditor, PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) { if (!aEditor) return; mTextEditor = do_QueryInterface(aEditor); @@ -98,9 +98,9 @@ nsresult TextEditorTest::RunUnitTest(PRInt32 *outNumTests, PRInt32 *outNumTestsF nsresult TextEditorTest::InitDoc() { - nsresult result = mTextEditor->SelectAll(); + nsresult result = mEditor->SelectAll(); TEST_RESULT(result); - result = mTextEditor->DeleteSelection(nsIEditor::eDeleteRight); + result = mEditor->DeleteSelection(nsIEditor::eDeleteNext); TEST_RESULT(result); return result; } @@ -165,14 +165,14 @@ nsresult TextEditorTest::TestTextProperties() PRBool any = PR_FALSE; PRBool all = PR_FALSE; PRBool first=PR_FALSE; - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_FALSE==first, "first should be false"); NS_ASSERTION(PR_FALSE==any, "any should be false"); NS_ASSERTION(PR_FALSE==all, "all should be false"); - result = mTextEditor->SetTextProperty(nsIEditProperty::b, nsnull, nsnull); + result = mTextEditor->SetInlineProperty(nsIEditProperty::b, nsnull, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); @@ -181,9 +181,9 @@ nsresult TextEditorTest::TestTextProperties() // remove the bold we just set printf("set the whole first text node to not bold\n"); - result = mTextEditor->RemoveTextProperty(nsIEditProperty::b, nsnull); + result = mTextEditor->RemoveInlineProperty(nsIEditProperty::b, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_FALSE==first, "first should be false"); NS_ASSERTION(PR_FALSE==any, "any should be false"); @@ -194,23 +194,23 @@ nsresult TextEditorTest::TestTextProperties() printf("set the first text node (1, length-1) to bold and italic, and (2, length-1) to underline.\n"); selection->Collapse(textNode, 1); selection->Extend(textNode, length-1); - result = mTextEditor->SetTextProperty(nsIEditProperty::b, nsnull, nsnull); + result = mTextEditor->SetInlineProperty(nsIEditProperty::b, nsnull, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); NS_ASSERTION(PR_TRUE==all, "all should be true"); mEditor->DebugDumpContent(); // make all that same text italic - result = mTextEditor->SetTextProperty(nsIEditProperty::i, nsnull, nsnull); + result = mTextEditor->SetInlineProperty(nsIEditProperty::i, nsnull, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::i, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::i, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); NS_ASSERTION(PR_TRUE==all, "all should be true"); - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); @@ -231,9 +231,9 @@ nsresult TextEditorTest::TestTextProperties() NS_ASSERTION(length==249, "wrong text node"); selection->Collapse(textNode, 1); selection->Extend(textNode, length-2); - result = mTextEditor->SetTextProperty(nsIEditProperty::u, nsnull, nsnull); + result = mTextEditor->SetInlineProperty(nsIEditProperty::u, nsnull, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::u, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::u, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); diff --git a/editor/base/TextEditorTest.h b/editor/base/TextEditorTest.h index 10bc575b8ed7..5d29022574b5 100644 --- a/editor/base/TextEditorTest.h +++ b/editor/base/TextEditorTest.h @@ -22,14 +22,14 @@ #ifdef NS_DEBUG #include "nsCOMPtr.h" -#include "nsITextEditor.h" #include "nsIEditor.h" +#include "nsIHTMLEditor.h" class TextEditorTest { public: - void Run(nsITextEditor *aEditor, PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); + void Run(nsIEditor *aEditor, PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); TextEditorTest(); ~TextEditorTest(); @@ -44,7 +44,7 @@ protected: nsresult TestTextProperties(); - nsCOMPtr mTextEditor; + nsCOMPtr mTextEditor; nsCOMPtr mEditor; }; diff --git a/editor/base/TypeInState.h b/editor/base/TypeInState.h index 2bea71928adc..9ae84010933b 100644 --- a/editor/base/TypeInState.h +++ b/editor/base/TypeInState.h @@ -16,8 +16,8 @@ * Reserved. */ -#ifndef ChangeAttributeTxn_h__ -#define ChangeAttributeTxn_h__ +#ifndef TypeInState_h__ +#define TypeInState_h__ #include "nsIDOMSelectionListener.h" #include "nsIEditProperty.h" @@ -228,4 +228,5 @@ inline void TypeInState::SetPropValue(PRUint32 aProp, const nsString &aValue) } } -#endif +#endif // TypeInState_h__ + diff --git a/editor/base/makefile.win b/editor/base/makefile.win index 45ada568e998..42ff185d0dc7 100644 --- a/editor/base/makefile.win +++ b/editor/base/makefile.win @@ -22,16 +22,16 @@ include <$(DEPTH)/config/config.mak> LIBRARY_NAME=ender CPPSRCS = \ - nsInsertHTMLTxn.cpp \ + nsInsertHTMLTxn.cpp \ nsEditor.cpp \ - nsTextEditor.cpp \ + nsEditorUtils.cpp \ + nsEditorRegistration.cpp \ nsTextEditRules.cpp \ nsHTMLEditRules.cpp \ TextEditorTest.cpp \ nsEditorEventListeners.cpp \ - nsEditProperty.cpp \ - nsEditFactory.cpp \ - nsTextEditFactory.cpp \ + nsEditProperty.cpp \ + nsEditorFactory.cpp \ EditTxn.cpp \ EditAggregateTxn.cpp \ ChangeAttributeTxn.cpp \ @@ -46,31 +46,31 @@ CPPSRCS = \ JoinElementTxn.cpp \ nsStyleSheetTxns.cpp \ TransactionFactory.cpp \ + TypeInState.cpp \ nsHTMLEditor.cpp \ - nsHTMLEditFactory.cpp \ EditTable.cpp \ - nsInternetCiter.cpp \ - nsAOLCiter.cpp \ - nsInterfaceState.cpp \ - nsEditorShell.cpp \ + nsInternetCiter.cpp \ + nsAOLCiter.cpp \ + nsInterfaceState.cpp \ + nsEditorShell.cpp \ nsEditorShellFactory.cpp \ - nsJSEditorLog.cpp \ - nsJSTxnLog.cpp \ - IMETextTxn.cpp \ - IMECommitTxn.cpp \ + nsJSEditorLog.cpp \ + nsJSTxnLog.cpp \ + IMETextTxn.cpp \ + IMECommitTxn.cpp \ $(NULL) CPP_OBJS = \ - .\$(OBJDIR)\nsInsertHTMLTxn.obj \ + .\$(OBJDIR)\nsInsertHTMLTxn.obj \ .\$(OBJDIR)\nsEditor.obj \ - .\$(OBJDIR)\nsTextEditor.obj \ + .\$(OBJDIR)\nsEditorUtils.obj \ + .\$(OBJDIR)\nsEditorRegistration.obj \ .\$(OBJDIR)\nsTextEditRules.obj \ .\$(OBJDIR)\TextEditorTest.obj \ .\$(OBJDIR)\nsHTMLEditRules.obj \ .\$(OBJDIR)\nsEditorEventListeners.obj \ .\$(OBJDIR)\nsEditProperty.obj \ - .\$(OBJDIR)\nsEditFactory.obj \ - .\$(OBJDIR)\nsTextEditFactory.obj \ + .\$(OBJDIR)\nsEditorFactory.obj \ .\$(OBJDIR)\EditTxn.obj \ .\$(OBJDIR)\EditAggregateTxn.obj \ .\$(OBJDIR)\ChangeAttributeTxn.obj \ @@ -85,17 +85,17 @@ CPP_OBJS = \ .\$(OBJDIR)\JoinElementTxn.obj \ .\$(OBJDIR)\nsStyleSheetTxns.obj \ .\$(OBJDIR)\TransactionFactory.obj \ + .\$(OBJDIR)\TypeInState.obj \ .\$(OBJDIR)\nsHTMLEditor.obj \ - .\$(OBJDIR)\nsHTMLEditFactory.obj \ .\$(OBJDIR)\EditTable.obj \ - .\$(OBJDIR)\nsInternetCiter.obj \ - .\$(OBJDIR)\nsAOLCiter.obj \ - .\$(OBJDIR)\nsInterfaceState.obj \ - .\$(OBJDIR)\nsEditorShell.obj \ + .\$(OBJDIR)\nsInternetCiter.obj \ + .\$(OBJDIR)\nsAOLCiter.obj \ + .\$(OBJDIR)\nsInterfaceState.obj \ + .\$(OBJDIR)\nsEditorShell.obj \ .\$(OBJDIR)\nsEditorShellFactory.obj \ .\$(OBJDIR)\nsJSEditorLog.obj \ .\$(OBJDIR)\nsJSTxnLog.obj \ - .\$(OBJDIR)\IMETextTxn.obj \ + .\$(OBJDIR)\IMETextTxn.obj \ .\$(OBJDIR)\IMECommitTxn.obj \ $(NULL) diff --git a/editor/base/nsEditRules.h b/editor/base/nsEditRules.h index 5152505ff5d5..8b32009b3385 100644 --- a/editor/base/nsEditRules.h +++ b/editor/base/nsEditRules.h @@ -19,7 +19,7 @@ #ifndef nsEditRules_h__ #define nsEditRules_h__ -class nsIEditor; +class nsHTMLEditor; class nsIDOMSelection; /*************************************************************************** @@ -43,9 +43,11 @@ class nsRulesInfo class nsEditRules { public: - NS_IMETHOD Init(nsIEditor *aEditor)=0; + NS_IMETHOD Init(nsHTMLEditor *aEditor)=0; NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel)=0; NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult)=0; + NS_IMETHOD GetFlags(PRUint32 *aFlags)=0; + NS_IMETHOD SetFlags(PRUint32 aFlags)=0; }; diff --git a/editor/base/nsEditor.cpp b/editor/base/nsEditor.cpp index 6497e9026f9d..79bd3e35aa9e 100644 --- a/editor/base/nsEditor.cpp +++ b/editor/base/nsEditor.cpp @@ -16,12 +16,14 @@ * Reserved. */ +#include "nsVector.h" +#include "nsVoidArray.h" #include "nsIDOMDocument.h" #include "nsIPref.h" #include "nsILocale.h" -#include "nsEditor.h" #include "nsIEditProperty.h" // to be removed XXX + #include "nsIDOMText.h" #include "nsIDOMElement.h" #include "nsIDOMAttr.h" @@ -31,12 +33,7 @@ #include "nsIDOMRange.h" #include "nsIDocument.h" #include "nsIDiskDocument.h" -#include "nsVector.h" #include "nsIServiceManager.h" -#include "nsEditFactory.h" -#include "nsTextEditFactory.h" -#include "nsHTMLEditFactory.h" -#include "nsEditorCID.h" #include "nsTransactionManagerCID.h" #include "nsITransactionManager.h" #include "nsIPresShell.h" @@ -45,7 +42,6 @@ #include "nsIDOMSelection.h" #include "nsIEnumerator.h" #include "nsIAtom.h" -#include "nsVoidArray.h" #include "nsISupportsArray.h" #include "nsICaret.h" #include "nsIStyleContext.h" @@ -55,9 +51,9 @@ #include "nsICSSStyleSheet.h" #include "nsIHTMLContentContainer.h" #include "nsIStyleSet.h" -#include "nsStyleSheetTxns.h" #include "nsIDocumentObserver.h" #include "nsIDocumentStateListener.h" +#include "nsIStringStream.h" #ifdef NECKO #include "nsNeckoUtil.h" @@ -65,9 +61,6 @@ #include "nsIURL.h" #endif // NECKO -#include "nsEditorShell.h" -#include "nsEditorShellFactory.h" - #include "nsIContent.h" #include "nsIContentIterator.h" #include "nsLayoutCID.h" @@ -89,12 +82,15 @@ #include "DeleteRangeTxn.h" #include "SplitElementTxn.h" #include "JoinElementTxn.h" -#include "nsIStringStream.h" +#include "nsStyleSheetTxns.h" #include "IMETextTxn.h" #include "IMECommitTxn.h" // #define HACK_FORCE_REDRAW 1 +#include "nsEditorCID.h" +#include "nsEditor.h" + #ifdef HACK_FORCE_REDRAW // INCLUDES FOR EVIL HACK TO FOR REDRAW @@ -106,32 +102,19 @@ // Drag & Drop, Clipboard #include "nsWidgetsCID.h" +#include "nsIFileWidget.h" #include "nsIClipboard.h" #include "nsITransferable.h" -// Drag & Drop, Clipboard Support -static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); -static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); - static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); -static NS_DEFINE_CID(kEditorCID, NS_EDITOR_CID); -static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); -static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); -static NS_DEFINE_IID(kEditorShellCID, NS_EDITORAPPCORE_CID); static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); // transaction manager static NS_DEFINE_CID(kCTransactionManagerCID, NS_TRANSACTIONMANAGER_CID); -static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID); -static NS_DEFINE_CID(kCDOMRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); static NS_DEFINE_CID(kPrefCID, NS_PREF_CID); -// factory classes -static NS_DEFINE_IID(kIEditFactoryIID, NS_IEDITORFACTORY_IID); -static NS_DEFINE_IID(kIHTMLEditFactoryIID, NS_IHTMLEDITORFACTORY_IID); -static NS_DEFINE_IID(kITextEditFactoryIID, NS_ITEXTEDITORFACTORY_IID); #ifdef XP_PC #define TRANSACTION_MANAGER_DLL "txmgr.dll" @@ -159,159 +142,8 @@ static const PRBool gNoisy = PR_FALSE; -/* ----- TEST METHODS DECLARATIONS ----- */ -// Methods defined here are TEMPORARY -//NS_IMETHODIMP GetColIndexForCell(nsIPresShell *aPresShell, nsIDOMNode *aCellNode, PRInt32 &aCellIndex); -/* ----- END TEST METHOD DECLARATIONS ----- */ - - PRInt32 nsEditor::gInstanceCount = 0; -//monitor for the editor - - - -PRMonitor *GetEditorMonitor() //if more than one person asks for the monitor at the same time for the FIRST time, we are screwed -{ - static PRMonitor *ns_editlock = nsnull; - if (nsnull == ns_editlock) - { - ns_editlock = (PRMonitor *)1; //how long will the next line take? lets cut down on the chance of reentrancy - ns_editlock = PR_NewMonitor(); - } - else if ((PRMonitor *)1 == ns_editlock) - return GetEditorMonitor(); - return ns_editlock; -} - -nsIComponentManager* gCompMgr = NULL; - -/* -we must be good providers of factories etc. this is where to put ALL editor exports -*/ -//BEGIN EXPORTS -extern "C" NS_EXPORT nsresult NSGetFactory(nsISupports * aServMgr, - const nsCID & aClass, - const char * aClassName, - const char * aProgID, - nsIFactory ** aFactory) -{ - if (nsnull == aFactory) { - return NS_ERROR_NULL_POINTER; - } - - *aFactory = nsnull; - - nsresult rv; - nsCOMPtr servMgr(do_QueryInterface(aServMgr, &rv)); - if (NS_FAILED(rv)) return rv; - - rv = servMgr->GetService(kComponentManagerCID, nsIComponentManager::GetIID(), - (nsISupports**)&gCompMgr); - if (NS_FAILED(rv)) return rv; - - rv = NS_NOINTERFACE; - - if (aClass.Equals(kEditorCID)) { - rv = GetEditFactory(aFactory, aClass); - if (NS_FAILED(rv)) goto done; - } - else if (aClass.Equals(kTextEditorCID)) { - rv = GetTextEditFactory(aFactory, aClass); - if (NS_FAILED(rv)) goto done; - } - else if (aClass.Equals(kHTMLEditorCID)) { - rv = GetHTMLEditFactory(aFactory, aClass); - if (NS_FAILED(rv)) goto done; - } - else if (aClass.Equals(kEditorShellCID)) { - rv = GetEditorShellFactory(aFactory, aClass, aClassName, aProgID); - if (NS_FAILED(rv)) goto done; - } - - done: - (void)servMgr->ReleaseService(kComponentManagerCID, gCompMgr); - - return rv; -} - - - -extern "C" NS_EXPORT PRBool -NSCanUnload(nsISupports* aServMgr) -{ - return nsEditor::gInstanceCount; //I have no idea. I am copying code here -} - - - -extern "C" NS_EXPORT nsresult -NSRegisterSelf(nsISupports* aServMgr, const char *path) -{ - nsresult rv; - nsCOMPtr servMgr(do_QueryInterface(aServMgr, &rv)); - if (NS_FAILED(rv)) return rv; - - nsIComponentManager* compMgr; - rv = servMgr->GetService(kComponentManagerCID, - nsIComponentManager::GetIID(), - (nsISupports**)&compMgr); - if (NS_FAILED(rv)) return rv; - - rv = compMgr->RegisterComponent(kEditorCID, NULL, NULL, path, - PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) goto done; - rv = compMgr->RegisterComponent(kTextEditorCID, NULL, NULL, path, - PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) goto done; - rv = compMgr->RegisterComponent(kHTMLEditorCID, NULL, NULL, path, - PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) goto done; - rv = compMgr->RegisterComponent(kEditorShellCID, - "Editor Shell Component", - "component://netscape/editor/editorshell", - path, PR_TRUE, PR_TRUE); - - if (NS_FAILED(rv)) goto done; - rv = compMgr->RegisterComponent(kEditorShellCID, - "Editor Shell Spell Checker", - "component://netscape/editor/editorspellcheck", - path, PR_TRUE, PR_TRUE); - - done: - (void)servMgr->ReleaseService(kComponentManagerCID, compMgr); - return rv; -} - -extern "C" NS_EXPORT nsresult -NSUnregisterSelf(nsISupports* aServMgr, const char *path) -{ - nsresult rv; - - nsCOMPtr servMgr(do_QueryInterface(aServMgr, &rv)); - if (NS_FAILED(rv)) return rv; - - nsIComponentManager* compMgr; - rv = servMgr->GetService(kComponentManagerCID, - nsIComponentManager::GetIID(), - (nsISupports**)&compMgr); - if (NS_FAILED(rv)) return rv; - - rv = compMgr->UnregisterComponent(kEditorCID, path); - if (NS_FAILED(rv)) goto done; - rv = compMgr->UnregisterComponent(kTextEditorCID, path); - if (NS_FAILED(rv)) goto done; - rv = compMgr->UnregisterComponent(kHTMLEditorCID, path); - if (NS_FAILED(rv)) goto done; - rv = compMgr->UnregisterComponent(kEditorShellCID, path); - - done: - (void)servMgr->ReleaseService(kComponentManagerCID, compMgr); - return rv; -} - -//END EXPORTS - //class implementations are in order they are declared in nsEditor.h @@ -333,11 +165,8 @@ nsEditor::nsEditor() PR_EnterMonitor(GetEditorMonitor()); gInstanceCount++; PR_ExitMonitor(GetEditorMonitor()); - } - - nsEditor::~nsEditor() { // not sure if this needs to be called earlier. @@ -371,22 +200,19 @@ nsEditor::~nsEditor() } - // BEGIN nsEditor core implementation - NS_IMPL_ADDREF(nsEditor) - NS_IMPL_RELEASE(nsEditor) - NS_IMETHODIMP nsEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) { - if (nsnull == aInstancePtr) { + if (!aInstancePtr) return NS_ERROR_NULL_POINTER; - } + + *aInstancePtr = nsnull; if (aIID.Equals(nsCOMTypeInfo::GetIID())) { nsIEditor *tmp = this; nsISupports *tmp2 = tmp; @@ -399,103 +225,34 @@ nsEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) NS_ADDREF_THIS(); return NS_OK; } + if (aIID.Equals(nsIEditorIMESupport::GetIID())) { + *aInstancePtr = (void*)(nsIEditorIMESupport*)this; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(nsIEditorLogging::GetIID())) { + *aInstancePtr = (void*)(nsIEditorLogging*)this; + NS_ADDREF_THIS(); + return NS_OK; + } + return NS_NOINTERFACE; } -NS_IMETHODIMP -nsEditor::GetDocument(nsIDOMDocument **aDoc) -{ - if (!aDoc) - return NS_ERROR_NULL_POINTER; - *aDoc = nsnull; // init out param - NS_PRECONDITION(mDoc, "bad state, null mDoc"); - if (!mDoc) - return NS_ERROR_NOT_INITIALIZED; - return mDoc->QueryInterface(nsIDOMDocument::GetIID(), (void **)aDoc); -} - -// This seems like too much work! There should be a "nsDOMDocument::GetBody()" -NS_IMETHODIMP -nsEditor::GetBodyElement(nsIDOMElement **aBodyElement) -{ - nsresult result; - - if (!aBodyElement) - return NS_ERROR_NULL_POINTER; - - *aBodyElement = 0; - - NS_PRECONDITION(mDoc, "bad state, null mDoc"); - if (!mDoc) - return NS_ERROR_NOT_INITIALIZED; - - nsCOMPtrnodeList; - nsString bodyTag = "body"; - - result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - - if (NS_FAILED(result)) - return result; - - if (!nodeList) - return NS_ERROR_NULL_POINTER; - - PRUint32 count; - nodeList->GetLength(&count); - - NS_ASSERTION(1==count, "More than one body found in document!"); - - if (count < 1) - return NS_ERROR_FAILURE; - - // Use the first body node in the list: - nsCOMPtr node; - result = nodeList->Item(0, getter_AddRefs(node)); - if (NS_SUCCEEDED(result) && node) - { - //return node->QueryInterface(nsIDOMElement::GetIID(), (void **)aBodyElement); - // Is above equivalent to this: - nsCOMPtr bodyElement = do_QueryInterface(node); - if (bodyElement) - { - *aBodyElement = bodyElement; - // A "getter" method should always addref - NS_ADDREF(*aBodyElement); - } - } - return result; -} - -nsresult -nsEditor::GetPresShell(nsIPresShell **aPS) -{ - if (!aPS) - return NS_ERROR_NULL_POINTER; - *aPS = nsnull; // init out param - NS_PRECONDITION(mPresShell, "bad state, null mPresShell"); - if (!mPresShell) - return NS_ERROR_NOT_INITIALIZED; - return mPresShell->QueryInterface(nsIPresShell::GetIID(), (void **)aPS); -} - +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorMethods --- +#pragma mark - +#endif NS_IMETHODIMP -nsEditor::GetSelection(nsIDOMSelection **aSelection) -{ - if (!aSelection) - return NS_ERROR_NULL_POINTER; - *aSelection = nsnull; - nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, aSelection); // does an addref - return result; -} - -NS_IMETHODIMP -nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell) +nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, PRUint32 aFlags) { NS_PRECONDITION(nsnull!=aDoc && nsnull!=aPresShell, "bad arg"); if ((nsnull==aDoc) || (nsnull==aPresShell)) return NS_ERROR_NULL_POINTER; + mFlags = aFlags; mDoc = aDoc; mPresShell = aPresShell; // we don't addref the pres shell @@ -559,29 +316,9 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell) if (NS_SUCCEEDED(result) && service) { -#if 1 nsILocale* locale = nsnull; result = service->CreateBundle(EDITOR_BUNDLE_URL, locale, getter_AddRefs(mStringBundle)); -#else - nsCOMPtr url; -#ifndef NECKO - result = NS_NewURL(getter_AddRefs(url), nsString(EDITOR_BUNDLE_URL)); -#else - result = NS_NewURI(getter_AddRefs(url), nsString(EDITOR_BUNDLE_URL)); -#endif // NECKO - - if (NS_SUCCEEDED(result) && url) - { - nsILocale* locale = nsnull; - result = service->CreateBundle(url, locale, getter_AddRefs(mStringBundle)); - if (NS_FAILED(result)) - printf("ERROR: Failed to get Create StringBundle\n"); - - } else { - printf("ERROR: Failed to get create URL for StringBundle\n"); - } -#endif // We don't need to keep service around once we created the bundle nsServiceManager::ReleaseService(kStringBundleServiceCID, service); } else { @@ -612,6 +349,158 @@ nsEditor::PostCreate() return NS_OK; } +NS_IMETHODIMP +nsEditor::GetDocument(nsIDOMDocument **aDoc) +{ + if (!aDoc) + return NS_ERROR_NULL_POINTER; + *aDoc = nsnull; // init out param + NS_PRECONDITION(mDoc, "bad state, null mDoc"); + if (!mDoc) + return NS_ERROR_NOT_INITIALIZED; + return mDoc->QueryInterface(nsIDOMDocument::GetIID(), (void **)aDoc); +} + + +nsresult +nsEditor::GetPresShell(nsIPresShell **aPS) +{ + if (!aPS) + return NS_ERROR_NULL_POINTER; + *aPS = nsnull; // init out param + NS_PRECONDITION(mPresShell, "bad state, null mPresShell"); + if (!mPresShell) + return NS_ERROR_NOT_INITIALIZED; + return mPresShell->QueryInterface(nsIPresShell::GetIID(), (void **)aPS); +} + + +NS_IMETHODIMP +nsEditor::GetSelection(nsIDOMSelection **aSelection) +{ + if (!aSelection) + return NS_ERROR_NULL_POINTER; + *aSelection = nsnull; + nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, aSelection); // does an addref + return result; +} + +static NS_DEFINE_IID(kCFileWidgetCID, NS_FILEWIDGET_CID); + +NS_IMETHODIMP nsEditor::SaveDocument(PRBool saveAs, PRBool saveCopy) +{ + nsresult rv = NS_OK; + + // get the document + nsCOMPtr doc; + rv = GetDocument(getter_AddRefs(doc)); + if (NS_FAILED(rv) || !doc) + return rv; + + nsCOMPtr diskDoc = do_QueryInterface(doc); + if (!diskDoc) + return NS_ERROR_NO_INTERFACE; + + // this should really call out to the appcore for the display of the put file + // dialog. + + // find out if the doc already has a fileSpec associated with it. + nsFileSpec docFileSpec; + PRBool mustShowFileDialog = saveAs || (diskDoc->GetFileSpec(docFileSpec) == NS_ERROR_NOT_INITIALIZED); + PRBool replacing = !saveAs; + + if (mustShowFileDialog) + { + nsCOMPtr fileWidget; + rv = nsComponentManager::CreateInstance(kCFileWidgetCID, nsnull, nsIFileWidget::GetIID(), getter_AddRefs(fileWidget)); + if (NS_SUCCEEDED(rv) && fileWidget) + { + nsAutoString promptString("Save this document as:"); // XXX i18n, l10n + nsFileDlgResults dialogResult; + dialogResult = fileWidget->PutFile(nsnull, promptString, docFileSpec); + if (dialogResult == nsFileDlgResults_Cancel) + return NS_OK; + + replacing = (dialogResult == nsFileDlgResults_Replace); + } + else + { + NS_ASSERTION(0, "Failed to get file widget"); + return rv; + } + } + + nsAutoString charsetStr("ISO-8859-1"); + rv = diskDoc->SaveFile(&docFileSpec, replacing, saveCopy, nsIDiskDocument::eSaveFileHTML, charsetStr); + + if (NS_FAILED(rv)) + { + // show some error dialog? + NS_WARNING("Saving file failed"); + } + + return rv; +} + +NS_IMETHODIMP nsEditor::Save() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->Save(); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult rv = SaveDocument(PR_FALSE, PR_FALSE); + if (NS_FAILED(rv)) + return rv; + + rv = DoAfterDocumentSave(); + return rv; +} + +NS_IMETHODIMP nsEditor::SaveAs(PRBool aSavingCopy) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SaveAs(aSavingCopy); +#endif // ENABLE_JS_EDITOR_LOG + + return SaveDocument(PR_TRUE, aSavingCopy); +} + + +NS_IMETHODIMP +nsEditor::Do(nsITransaction *aTxn) +{ + if (gNoisy) { printf("Editor::Do ----------\n"); } + nsresult result = NS_OK; + nsCOMPtrselection; + nsresult selectionResult = GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + if (aTxn) + { + if (mTxnMgr) { + result = mTxnMgr->Do(aTxn); + } + else { + result = aTxn->Do(); + } + + if (NS_SUCCEEDED(result)) + result = DoAfterDoTransaction(aTxn); + } + + selection->EndBatchChanges(); + } + + return result; +} + + NS_IMETHODIMP nsEditor::EnableUndo(PRBool aEnable) { @@ -621,7 +510,7 @@ nsEditor::EnableUndo(PRBool aEnable) { if (!mTxnMgr) { - result = gCompMgr->CreateInstance(kCTransactionManagerCID, + result = nsComponentManager::CreateInstance(kCTransactionManagerCID, nsnull, nsITransactionManager::GetIID(), getter_AddRefs(mTxnMgr)); if (NS_FAILED(result) || !mTxnMgr) { @@ -643,6 +532,43 @@ nsEditor::EnableUndo(PRBool aEnable) return result; } + +NS_IMETHODIMP +nsEditor::Undo(PRUint32 aCount) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->Undo(aCount); +#endif // ENABLE_JS_EDITOR_LOG + + if (gNoisy) { printf("Editor::Undo ----------\n"); } + nsresult result = NS_OK; + + BeginUpdateViewBatch(); + + if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) + { + PRUint32 i=0; + for ( ; iUndo(); + + if (NS_SUCCEEDED(result)) + result = DoAfterUndoTransaction(); + + if (NS_FAILED(result)) + break; + } + } + + EndUpdateViewBatch(); + + return result; +} + + NS_IMETHODIMP nsEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo) { aIsEnabled = ((PRBool)((nsITransactionManager *)0!=mTxnMgr.get())); @@ -658,6 +584,43 @@ NS_IMETHODIMP nsEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo) return NS_OK; } + +NS_IMETHODIMP +nsEditor::Redo(PRUint32 aCount) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->Redo(aCount); +#endif // ENABLE_JS_EDITOR_LOG + + if (gNoisy) { printf("Editor::Redo ----------\n"); } + nsresult result = NS_OK; + + BeginUpdateViewBatch(); + + if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) + { + PRUint32 i=0; + for ( ; iRedo(); + + if (NS_SUCCEEDED(result)) + result = DoAfterRedoTransaction(); + + if (NS_FAILED(result)) + break; + } + } + + EndUpdateViewBatch(); + + return result; +} + + NS_IMETHODIMP nsEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo) { aIsEnabled = ((PRBool)((nsITransactionManager *)0!=mTxnMgr.get())); @@ -673,10 +636,181 @@ NS_IMETHODIMP nsEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo) return NS_OK; } + +NS_IMETHODIMP +nsEditor::BeginTransaction() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->BeginTransaction(); +#endif // ENABLE_JS_EDITOR_LOG + + BeginUpdateViewBatch(); + + if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) + { + mTxnMgr->BeginBatch(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsEditor::EndTransaction() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->EndTransaction(); +#endif // ENABLE_JS_EDITOR_LOG + + if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) + { + mTxnMgr->EndBatch(); + } + + EndUpdateViewBatch(); + + return NS_OK; +} + +// XXX: the rule system should tell us which node to select all on (ie, the root, or the body) +NS_IMETHODIMP nsEditor::SelectAll() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SelectAll(); +#endif // ENABLE_JS_EDITOR_LOG + + if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + result = SelectEntireDocument(selection); + } + return result; +} + +NS_IMETHODIMP nsEditor::BeginningOfDocument() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->BeginningOfDocument(); +#endif // ENABLE_JS_EDITOR_LOG + + if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + nsCOMPtr nodeList; + nsAutoString bodyTag = "body"; + result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); + if ((NS_SUCCEEDED(result)) && nodeList) + { + PRUint32 count; + nodeList->GetLength(&count); + NS_VERIFY(1==count, "there is not exactly 1 body in the document!"); + nsCOMPtr bodyNode; + result = nodeList->Item(0, getter_AddRefs(bodyNode)); + if ((NS_SUCCEEDED(result)) && bodyNode) + { + // Get the first child of the body node: + nsCOMPtr firstNode = GetDeepFirstChild(bodyNode); + if (firstNode) + { + result = selection->Collapse(firstNode, 0); + ScrollIntoView(PR_TRUE); + } + } + } + } + return result; +} + +NS_IMETHODIMP nsEditor::EndOfDocument() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->EndOfDocument(); +#endif // ENABLE_JS_EDITOR_LOG + + if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + nsCOMPtr nodeList; + nsAutoString bodyTag = "body"; + result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); + if ((NS_SUCCEEDED(result)) && nodeList) + { + PRUint32 count; + nodeList->GetLength(&count); + NS_VERIFY(1==count, "there is not exactly 1 body in the document!"); + nsCOMPtr bodyNode; + result = nodeList->Item(0, getter_AddRefs(bodyNode)); + if ((NS_SUCCEEDED(result)) && bodyNode) + { + nsCOMPtr lastChild = GetDeepLastChild(bodyNode); + if ((NS_SUCCEEDED(result)) && lastChild) + { + // See if the last child is a text node; if so, set offset: + PRUint32 offset = 0; + if (IsTextNode(lastChild)) + { + nsCOMPtr text (do_QueryInterface(lastChild)); + if (text) + text->GetLength(&offset); + } + result = selection->Collapse(lastChild, offset); + ScrollIntoView(PR_FALSE); + } + } + } + } + return result; +} + +NS_IMETHODIMP +nsEditor::GetDocumentModified(PRBool *outDocModified) +{ + if (!outDocModified) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr theDoc; + nsresult rv = GetDocument(getter_AddRefs(theDoc)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr diskDoc = do_QueryInterface(theDoc, &rv); + if (NS_FAILED(rv)) return rv; + + PRInt32 modCount = 0; + diskDoc->GetModCount(&modCount); + + *outDocModified = (modCount != 0); + return NS_OK; +} + +/* + NS_IMETHODIMP nsEditor::SetProperties(nsVoidArray * aPropList) { - return NS_OK; + return NS_ERROR_NOT_IMPLEMENTED; } @@ -684,9 +818,9 @@ nsEditor::SetProperties(nsVoidArray * aPropList) NS_IMETHODIMP nsEditor::GetProperties(nsVoidArray *aPropList) { - return NS_OK; + return NS_ERROR_NOT_IMPLEMENTED; } - +*/ NS_IMETHODIMP nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, const nsString& aValue) @@ -700,23 +834,6 @@ nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, cons } -NS_IMETHODIMP -nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement, - const nsString& aAttribute, - const nsString& aValue, - ChangeAttributeTxn ** aTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (nsnull != aElement) - { - result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(this, aElement, aAttribute, aValue, PR_FALSE); - } - } - return result; -} - NS_IMETHODIMP nsEditor::GetAttributeValue(nsIDOMElement *aElement, const nsString& aAttribute, @@ -749,24 +866,568 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute) return result; } -NS_IMETHODIMP -nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, - const nsString& aAttribute, - ChangeAttributeTxn ** aTxn) + +NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, + nsIDOMNode * aParent, + PRInt32 aPosition, + nsIDOMNode ** aNewNode) { - nsresult result = NS_ERROR_NULL_POINTER; - if (nsnull != aElement) + CreateElementTxn *txn; + nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition, &txn); + if (NS_SUCCEEDED(result)) { - result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) + result = Do(txn); + if (NS_SUCCEEDED(result)) { - nsAutoString value; - result = (*aTxn)->Init(this, aElement, aAttribute, value, PR_TRUE); + result = txn->GetNewNode(aNewNode); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::Do succeeded."); } } return result; } + +NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, + nsIDOMNode * aParent, + PRInt32 aPosition) +{ + PRInt32 i; + nsIEditActionListener *listener; + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillInsertNode(aNode, aParent, aPosition); + } + } + + InsertElementTxn *txn; + nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition, &txn); + if (NS_SUCCEEDED(result)) { + result = Do(txn); + } + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidInsertNode(aNode, aParent, aPosition, result); + } + } + + return result; +} + + +NS_IMETHODIMP +nsEditor::SplitNode(nsIDOMNode * aNode, + PRInt32 aOffset, + nsIDOMNode **aNewLeftNode) +{ + PRInt32 i; + nsIEditActionListener *listener; + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillSplitNode(aNode, aOffset); + } + } + + SplitElementTxn *txn; + nsresult result = CreateTxnForSplitNode(aNode, aOffset, &txn); + if (NS_SUCCEEDED(result)) + { + result = Do(txn); + if (NS_SUCCEEDED(result)) + { + result = txn->GetNewNode(aNewLeftNode); + NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode"); + } + } + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + { + nsIDOMNode *ptr = (aNewLeftNode) ? *aNewLeftNode : 0; + listener->DidSplitNode(aNode, aOffset, ptr, result); + } + } + } + + return result; +} + + +NS_IMETHODIMP +nsEditor::JoinNodes(nsIDOMNode * aLeftNode, + nsIDOMNode * aRightNode, + nsIDOMNode * aParent) +{ + PRInt32 i; + nsIEditActionListener *listener; + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillJoinNodes(aLeftNode, aRightNode, aParent); + } + } + + JoinElementTxn *txn; + nsresult result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn); + if (NS_SUCCEEDED(result)) { + result = Do(txn); + } + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidJoinNodes(aLeftNode, aRightNode, aParent, result); + } + } + + return result; +} + + +NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) +{ + PRInt32 i; + nsIEditActionListener *listener; + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillDeleteNode(aElement); + } + } + + DeleteElementTxn *txn; + nsresult result = CreateTxnForDeleteElement(aElement, &txn); + if (NS_SUCCEEDED(result)) { + result = Do(txn); + } + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidDeleteNode(aElement, result); + } + } + + return result; +} + + +NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags) +{ + // these should be implemented by derived classes. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags) +{ + // these should be implemented by derived classes. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsEditor::AddEditActionListener(nsIEditActionListener *aListener) +{ + if (!aListener) + return NS_ERROR_NULL_POINTER; + + if (!mActionListeners) + { + mActionListeners = new nsVoidArray(); + + if (!mActionListeners) + return NS_ERROR_OUT_OF_MEMORY; + } + + if (!mActionListeners->AppendElement((void *)aListener)) + return NS_ERROR_FAILURE; + + NS_ADDREF(aListener); + + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener) +{ + if (!aListener || !mActionListeners) + return NS_ERROR_FAILURE; + + if (!mActionListeners->RemoveElement((void *)aListener)) + return NS_ERROR_FAILURE; + + NS_IF_RELEASE(aListener); + + if (mActionListeners->Count() < 1) + { + delete mActionListeners; + mActionListeners = 0; + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener) +{ + if (!aListener) + return NS_ERROR_NULL_POINTER; + + nsresult rv = NS_OK; + + if (!mDocStateListeners) + { + rv = NS_NewISupportsArray(getter_AddRefs(mDocStateListeners)); + if (NS_FAILED(rv)) return rv; + } + + nsCOMPtr iSupports = do_QueryInterface(aListener, &rv); + if (NS_FAILED(rv)) return rv; + + // is it already in the list? + PRInt32 foundIndex; + if (NS_SUCCEEDED(mDocStateListeners->GetIndexOf(iSupports, &foundIndex)) && foundIndex != -1) + return NS_OK; + + return mDocStateListeners->AppendElement(iSupports); +} + + +NS_IMETHODIMP +nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener) +{ + if (!aListener || !mDocStateListeners) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + nsCOMPtr iSupports = do_QueryInterface(aListener, &rv); + if (NS_FAILED(rv)) return rv; + + return mDocStateListeners->RemoveElement(iSupports); +} + + +NS_IMETHODIMP +nsEditor::DumpContentTree() +{ + nsCOMPtr thedoc; + nsCOMPtr presShell; + if (NS_SUCCEEDED(GetPresShell(getter_AddRefs(presShell)))) + { + presShell->GetDocument(getter_AddRefs(thedoc)); + if (thedoc) { + nsIContent* root = thedoc->GetRootContent(); + if (nsnull != root) { + root->List(stdout); + NS_RELEASE(root); + } + } + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::DebugDumpContent() const +{ + nsCOMPtrcontent; + nsCOMPtrnodeList; + nsAutoString bodyTag = "body"; + mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); + if (nodeList) + { + PRUint32 count; + nodeList->GetLength(&count); + NS_ASSERTION(1==count, "there is not exactly 1 body in the document!"); + nsCOMPtrbodyNode; + nodeList->Item(0, getter_AddRefs(bodyNode)); + if (bodyNode) { + content = do_QueryInterface(bodyNode); + } + } + content->List(); + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) +{ + NS_NOTREACHED("This should never get called. Overridden by subclasses"); + return NS_OK; +} + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorIMESupport --- +#pragma mark - +#endif + +// +// The BeingComposition method is called from the Editor Composition event listeners. +// It caches the current text node and offset which is subsequently used for the +// created of IMETextTxn's. +// +NS_IMETHODIMP +nsEditor::BeginComposition(void) +{ +#ifdef DEBUG_tague + printf("nsEditor::StartComposition\n"); +#endif + nsresult result; + PRInt32 offset; + nsCOMPtr selection; + nsCOMPtr nodeAsText; + + result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + result = NS_ERROR_UNEXPECTED; + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && (currentItem)) + { + result = NS_ERROR_UNEXPECTED; + nsCOMPtr range(do_QueryInterface(currentItem)); + if (range) + { + nsCOMPtr node; + result = range->GetStartParent(getter_AddRefs(node)); + if ((NS_SUCCEEDED(result)) && (node)) + { + nodeAsText = do_QueryInterface(node); + range->GetStartOffset(&offset); + if (!nodeAsText) { + result = NS_ERROR_EDITOR_NO_TEXTNODE; + } + } + } + } + else + { + result = NS_ERROR_EDITOR_NO_SELECTION; + } + } + } + + if (NS_SUCCEEDED(result) && nodeAsText) + { + // + // store the information needed to construct IME transactions for this composition + // + mIMETextNode = nodeAsText; + mIMETextOffset = offset; + mIMEBufferLength = 0; + } + + return result; +} + +NS_IMETHODIMP +nsEditor::EndComposition(void) +{ + nsresult result; + IMECommitTxn *commitTxn; + + // + // create the commit transaction..we can do it directly from the transaction mgr + // + result = TransactionFactory::GetNewTransaction(IMECommitTxn::GetCID(), (EditTxn**)&commitTxn); + if (NS_SUCCEEDED(result) && commitTxn!=nsnull) + { + commitTxn->Init(); + result = Do(commitTxn); + } + + /* reset the data we need to construct a transaction */ + mIMETextNode = do_QueryInterface(nsnull); + mIMETextOffset = 0; + mIMEBufferLength = 0; + + return result; +} + +NS_IMETHODIMP +nsEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply) +{ + nsCOMPtr caretP; + nsresult result = SetInputMethodText(aCompositionString,aTextRangeList); + mIMEBufferLength = aCompositionString.Length(); + + mPresShell->GetCaret(getter_AddRefs(caretP)); + caretP->GetWindowRelativeCoordinates(aReply->mCursorPosition,aReply->mCursorIsCollapsed); + + return result; +} + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorLogging --- +#pragma mark - +#endif + + +NS_IMETHODIMP +nsEditor::StartLogging(nsIFileSpec *aLogFile) +{ +#ifdef ENABLE_JS_EDITOR_LOG + + mJSEditorLog = new nsJSEditorLog(this, aLogFile); + + if (!mJSEditorLog) + return NS_ERROR_OUT_OF_MEMORY; + + if (mTxnMgr) + { + mJSTxnLog = new nsJSTxnLog(mJSEditorLog); + + if (mJSTxnLog) + { + NS_ADDREF(mJSTxnLog); + mTxnMgr->AddListener(mJSTxnLog); + } + else + return NS_ERROR_OUT_OF_MEMORY; + } + +#endif // ENABLE_JS_EDITOR_LOG + + return NS_OK; +} + +NS_IMETHODIMP +nsEditor::StopLogging() +{ +#ifdef ENABLE_JS_EDITOR_LOG + + if (mTxnMgr && mJSTxnLog) + mTxnMgr->RemoveListener(mJSTxnLog); + + if (mJSTxnLog) + { + NS_RELEASE(mJSTxnLog); + mJSTxnLog = 0; + } + + if (mJSEditorLog) + { + delete mJSEditorLog; + mJSEditorLog = 0; + } + +#endif // ENABLE_JS_EDITOR_LOG + + return NS_OK; +} + + + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- public nsEditor methods --- +#pragma mark - +#endif +/* Non-interface, public methods */ + + +// This seems like too much work! There should be a "nsDOMDocument::GetBody()" +// Have a look in nsHTMLDocument. Maybe add it to nsIHTMLDocument. +NS_IMETHODIMP +nsEditor::GetBodyElement(nsIDOMElement **aBodyElement) +{ + nsresult result; + + if (!aBodyElement) + return NS_ERROR_NULL_POINTER; + + *aBodyElement = 0; + + NS_PRECONDITION(mDoc, "bad state, null mDoc"); + if (!mDoc) + return NS_ERROR_NOT_INITIALIZED; + + nsCOMPtrnodeList; + nsString bodyTag = "body"; + + result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); + + if (NS_FAILED(result)) + return result; + + if (!nodeList) + return NS_ERROR_NULL_POINTER; + + PRUint32 count; + nodeList->GetLength(&count); + + NS_ASSERTION(1==count, "More than one body found in document!"); + + if (count < 1) + return NS_ERROR_FAILURE; + + // Use the first body node in the list: + nsCOMPtr node; + result = nodeList->Item(0, getter_AddRefs(node)); + if (NS_SUCCEEDED(result) && node) + { + //return node->QueryInterface(nsIDOMElement::GetIID(), (void **)aBodyElement); + // Is above equivalent to this: + nsCOMPtr bodyElement = do_QueryInterface(node); + if (bodyElement) + { + *aBodyElement = bodyElement; + // A "getter" method should always addref + NS_ADDREF(*aBodyElement); + } + } + return result; +} + + // Objects must be DOM elements NS_IMETHODIMP nsEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) @@ -840,235 +1501,88 @@ nsEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) return result; } -NS_IMETHODIMP -nsEditor::InsertBreak() -{ - return NS_OK; -} - -//END nsIEditorInterfaces - - -//BEGIN nsEditor Private methods - -NS_IMETHODIMP -nsEditor::DoAfterDoTransaction(nsITransaction *aTxn) -{ - nsresult rv = NS_OK; - - PRBool isTransientTransaction; - rv = aTxn->GetIsTransient(&isTransientTransaction); - if (NS_FAILED(rv)) - return rv; - - if (!isTransientTransaction) - { - // we need to deal here with the case where the user saved after some - // edits, then undid one or more times. Then, the undo count is -ve, - // but we can't let a do take it back to zero. So we flip it up to - // a +ve number. - PRInt32 modCount; - GetDocModCount(modCount); - if (modCount < 0) - modCount = -modCount; - - rv = IncDocModCount(1); // don't count transient transactions - } - - return rv; -} - - -NS_IMETHODIMP -nsEditor::DoAfterUndoTransaction() -{ - nsresult rv = NS_OK; - - rv = IncDocModCount(-1); // all undoable transactions are non-transient - - return rv; -} - -NS_IMETHODIMP -nsEditor::DoAfterRedoTransaction() -{ - nsresult rv = NS_OK; - - rv = IncDocModCount(1); // all redoable transactions are non-transient - - return rv; -} - -NS_IMETHODIMP -nsEditor::DoAfterDocumentSave() -{ - // the mod count is reset by nsIDiskDocument. - NotifyDocumentListeners(eDocumentStateChanged); - return NS_OK; -} - - -NS_IMETHODIMP -nsEditor::Do(nsITransaction *aTxn) -{ - if (gNoisy) { printf("Editor::Do ----------\n"); } - nsresult result = NS_OK; - nsCOMPtrselection; - nsresult selectionResult = GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(selectionResult) && selection) { - selection->StartBatchChanges(); - if (aTxn) - { - if (mTxnMgr) { - result = mTxnMgr->Do(aTxn); - } - else { - result = aTxn->Do(); - } - - if (NS_SUCCEEDED(result)) - result = DoAfterDoTransaction(aTxn); - } - - selection->EndBatchChanges(); - } - - return result; -} - -NS_IMETHODIMP -nsEditor::Undo(PRUint32 aCount) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Undo(aCount); -#endif // ENABLE_JS_EDITOR_LOG - - if (gNoisy) { printf("Editor::Undo ----------\n"); } - nsresult result = NS_OK; - - BeginUpdateViewBatch(); - - if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) - { - PRUint32 i=0; - for ( ; iUndo(); - - if (NS_SUCCEEDED(result)) - result = DoAfterUndoTransaction(); - - if (NS_FAILED(result)) - break; - } - } - - EndUpdateViewBatch(); - - return result; -} - -NS_IMETHODIMP -nsEditor::Redo(PRUint32 aCount) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Redo(aCount); -#endif // ENABLE_JS_EDITOR_LOG - - if (gNoisy) { printf("Editor::Redo ----------\n"); } - nsresult result = NS_OK; - - BeginUpdateViewBatch(); - - if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) - { - PRUint32 i=0; - for ( ; iRedo(); - - if (NS_SUCCEEDED(result)) - result = DoAfterRedoTransaction(); - - if (NS_FAILED(result)) - break; - } - } - - EndUpdateViewBatch(); - - return result; -} - -NS_IMETHODIMP -nsEditor::BeginTransaction() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->BeginTransaction(); -#endif // ENABLE_JS_EDITOR_LOG - - BeginUpdateViewBatch(); - - if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) - { - mTxnMgr->BeginBatch(); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::EndTransaction() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->EndTransaction(); -#endif // ENABLE_JS_EDITOR_LOG - - if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) - { - mTxnMgr->EndBatch(); - } - - EndUpdateViewBatch(); - - return NS_OK; -} +#ifdef XP_MAC +#pragma mark - +#pragma mark --- Protected and static methods --- +#pragma mark - +#endif NS_IMETHODIMP nsEditor::ScrollIntoView(PRBool aScrollToBegin) { - return NS_OK; + return NS_ERROR_NOT_IMPLEMENTED; } -// XXX: the rule system should tell us which node to select all on (ie, the root, or the body) -NS_IMETHODIMP nsEditor::SelectAll() + +nsString& nsEditor::GetTextNodeTag() +{ + static nsString gTextNodeTag("special text node tag"); + return gTextNodeTag; +} + + +NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) { #ifdef ENABLE_JS_EDITOR_LOG nsAutoJSEditorLogLock logLock(mJSEditorLog); if (mJSEditorLog) - mJSEditorLog->SelectAll(); + mJSEditorLog->InsertText(aStringToInsert); + #endif // ENABLE_JS_EDITOR_LOG - if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) + EditAggregateTxn *aggTxn = nsnull; + // Create the "delete current selection" txn + nsresult result = CreateAggregateTxnForDeleteSelection(InsertTextTxn::gInsertTextTxnName, &aggTxn); + if ((NS_FAILED(result)) || (nsnull==aggTxn)) { + return NS_ERROR_OUT_OF_MEMORY; + } + InsertTextTxn *txn; + result = CreateTxnForInsertText(aStringToInsert, nsnull, &txn); // insert at the current selection + if ((NS_SUCCEEDED(result)) && txn) { + BeginUpdateViewBatch(); + aggTxn->AppendChild(txn); + result = Do(aggTxn); + EndUpdateViewBatch(); + } + else if (NS_ERROR_EDITOR_NO_SELECTION==result) { + result = DoInitialInsert(aStringToInsert); + } + else if (NS_ERROR_EDITOR_NO_TEXTNODE==result) { - result = SelectEntireDocument(selection); + BeginTransaction(); + result = Do(aggTxn); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + result = GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + nsCOMPtr selectedNode; + PRInt32 offset; + result = selection->GetAnchorNode(getter_AddRefs(selectedNode)); + if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offset)) && selectedNode) + { + nsCOMPtr newNode; + result = CreateNode(GetTextNodeTag(), selectedNode, offset, + getter_AddRefs(newNode)); + if (NS_SUCCEEDED(result) && newNode) + { + nsCOMPtrnewTextNode; + newTextNode = do_QueryInterface(newNode); + if (newTextNode) + { + nsAutoString placeholderText(" "); + newTextNode->SetData(placeholderText); + selection->Collapse(newNode, 0); + selection->Extend(newNode, 1); + result = InsertTextImpl(aStringToInsert); // this really recurses, right? + } + } + } + } + } + EndTransaction(); } return result; } @@ -1134,45 +1648,6 @@ nsEditor::GetDeepFirstChild(nsCOMPtr aRoot) return deepFirstChildN; } -NS_IMETHODIMP nsEditor::BeginningOfDocument() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->BeginningOfDocument(); -#endif // ENABLE_JS_EDITOR_LOG - - if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) - { - nsCOMPtr nodeList; - nsAutoString bodyTag = "body"; - result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if ((NS_SUCCEEDED(result)) && nodeList) - { - PRUint32 count; - nodeList->GetLength(&count); - NS_VERIFY(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtr bodyNode; - result = nodeList->Item(0, getter_AddRefs(bodyNode)); - if ((NS_SUCCEEDED(result)) && bodyNode) - { - // Get the first child of the body node: - nsCOMPtr firstNode = GetDeepFirstChild(bodyNode); - if (firstNode) - { - result = selection->Collapse(firstNode, 0); - ScrollIntoView(PR_TRUE); - } - } - } - } - return result; -} nsCOMPtr nsEditor::GetDeepLastChild(nsCOMPtr aRoot) @@ -1212,430 +1687,7 @@ nsEditor::GetDeepLastChild(nsCOMPtr aRoot) return deepLastChildN; } -NS_IMETHODIMP nsEditor::EndOfDocument() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - if (mJSEditorLog) - mJSEditorLog->EndOfDocument(); -#endif // ENABLE_JS_EDITOR_LOG - - if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) - { - nsCOMPtr nodeList; - nsAutoString bodyTag = "body"; - result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if ((NS_SUCCEEDED(result)) && nodeList) - { - PRUint32 count; - nodeList->GetLength(&count); - NS_VERIFY(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtr bodyNode; - result = nodeList->Item(0, getter_AddRefs(bodyNode)); - if ((NS_SUCCEEDED(result)) && bodyNode) - { - nsCOMPtr lastChild = GetDeepLastChild(bodyNode); - if ((NS_SUCCEEDED(result)) && lastChild) - { - // See if the last child is a text node; if so, set offset: - PRUint32 offset = 0; - if (IsTextNode(lastChild)) - { - nsCOMPtr text (do_QueryInterface(lastChild)); - if (text) - text->GetLength(&offset); - } - result = selection->Collapse(lastChild, offset); - ScrollIntoView(PR_FALSE); - } - } - } - } - return result; -} - -NS_IMETHODIMP nsEditor::Cut() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Cut(); -#endif // ENABLE_JS_EDITOR_LOG - - nsCOMPtr selection; - nsresult res = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (!NS_SUCCEEDED(res)) - return res; - - PRBool isCollapsed; - if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed) - return NS_ERROR_NOT_AVAILABLE; - - res = Copy(); - if (NS_SUCCEEDED(res)) - res = DeleteSelection(eDoNothing); - return res; -} - - -NS_IMETHODIMP nsEditor::Copy() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Copy(); -#endif // ENABLE_JS_EDITOR_LOG - - //printf("nsEditor::Copy\n"); - - return mPresShell->DoCopy(); -} - -NS_IMETHODIMP nsEditor::Paste() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Paste(); -#endif // ENABLE_JS_EDITOR_LOG - - //printf("nsEditor::Paste\n"); - nsString stuffToPaste; - - // Get Clipboard Service - nsIClipboard* clipboard; - nsresult rv = nsServiceManager::GetService(kCClipboardCID, - nsIClipboard::GetIID(), - (nsISupports **)&clipboard); - - // Create generic Transferable for getting the data - nsCOMPtr trans; - rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, - nsITransferable::GetIID(), - (void**) getter_AddRefs(trans)); - if (NS_SUCCEEDED(rv)) - { - // Get the nsITransferable interface for getting the data from the clipboard - if (trans) - { - // The only data type we support is plaintext; - // derived classes will support other types. - nsAutoString textFlavor(kTextMime); - trans->AddDataFlavor(&textFlavor); - - // Get the Data from the clipboard - if (NS_SUCCEEDED(clipboard->GetData(trans))) - { - nsAutoString flavor; - char * data; - PRUint32 len; - if (NS_SUCCEEDED(trans->GetAnyTransferData(&flavor, (void **)&data, &len))) - { -#ifdef DEBUG - printf("Got flavor [%s]\n", flavor.ToNewCString()); -#endif - if (flavor.Equals(textFlavor)) - { - if (data && len > 0) // stuffToPaste is ready for insertion into the content - { - stuffToPaste.SetString(data, len); - rv = InsertText(stuffToPaste); - } - } - } - - } - } - } - nsServiceManager::ReleaseService(kCClipboardCID, clipboard); - - return rv; -} - -NS_IMETHODIMP nsEditor::PasteAsQuotation() -{ -#ifdef DEBUG - printf("nsEditor::PasteAsQuotation() not meaningful, shouldn't be here\n"); -#endif - return Paste(); -} - -NS_IMETHODIMP nsEditor::InsertAsQuotation(const nsString& aQuotedText) -{ -#ifdef DEBUG - printf("nsEditor::PasteAsQuotation() not meaningful, shouldn't be here\n"); -#endif - return InsertText(aQuotedText); -} - -NS_IMETHODIMP -nsEditor::AddStyleSheet(nsICSSStyleSheet* aSheet) -{ - AddStyleSheetTxn* aTxn; - nsresult rv = CreateTxnForAddStyleSheet(aSheet, &aTxn); - if (NS_SUCCEEDED(rv) && aTxn) - { - rv = Do(aTxn); - if (NS_SUCCEEDED(rv)) - { - mLastStyleSheet = do_QueryInterface(aSheet); // save it so we can remove before applying the next one - } - } - - return rv; -} - - -NS_IMETHODIMP -nsEditor::RemoveStyleSheet(nsICSSStyleSheet* aSheet) -{ - RemoveStyleSheetTxn* aTxn; - nsresult rv = CreateTxnForRemoveStyleSheet(aSheet, &aTxn); - if (NS_SUCCEEDED(rv) && aTxn) - { - rv = Do(aTxn); - if (NS_SUCCEEDED(rv)) - { - mLastStyleSheet = nsnull; // forget it - } - } - - return rv; -} - -NS_IMETHODIMP -nsEditor::ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet) -{ - nsresult rv = NS_OK; - - BeginTransaction(); - - if (mLastStyleSheet) - { - rv = RemoveStyleSheet(mLastStyleSheet); - } - - rv = AddStyleSheet(aNewSheet); - - EndTransaction(); - - return rv; -} - -/* static */ -void nsEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData) -{ - nsresult rv = NS_OK; - - nsEditor *editor = NS_STATIC_CAST(nsEditor*, aData); - if (editor) - { - rv = editor->ReplaceStyleSheet(aSheet); - } - - // we lose the return value here. Set a flag in the editor? -} - -NS_IMETHODIMP nsEditor::ApplyStyleSheet(const nsString& aURL) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->ApplyStyleSheet(aURL); -#endif // ENABLE_JS_EDITOR_LOG - - // XXX: Note that this is not an undo-able action yet! - - nsresult rv = NS_OK; - nsIURI* uaURL = 0; - -#ifndef NECKO - rv = NS_NewURL(&uaURL, aURL); -#else - rv = NS_NewURI(&uaURL, aURL); -#endif // NECKO - - if (NS_SUCCEEDED(rv)) { - nsCOMPtr document; - - rv = mPresShell->GetDocument(getter_AddRefs(document)); - - if (NS_SUCCEEDED(rv)) { - if (document) { - nsCOMPtr container = do_QueryInterface(document); - - if (container) { - nsICSSLoader *cssLoader = 0; - nsICSSStyleSheet *cssStyleSheet = 0; - - rv = container->GetCSSLoader(cssLoader); - - if (NS_SUCCEEDED(rv)) { - if (cssLoader) { - PRBool complete; - - rv = cssLoader->LoadAgentSheet(uaURL, cssStyleSheet, complete, - nsEditor::ApplyStyleSheetToPresShellDocument, - this); - - if (NS_SUCCEEDED(rv)) { - if (complete) { - if (cssStyleSheet) { - nsEditor::ApplyStyleSheetToPresShellDocument(cssStyleSheet, - this); - } - else - rv = NS_ERROR_NULL_POINTER; - } - - // - // If not complete, we will be notified later - // with a call to AddStyleSheetToEditorDocument(). - // - } - } - else - rv = NS_ERROR_NULL_POINTER; - } - } - else - rv = NS_ERROR_NULL_POINTER; - } - else - rv = NS_ERROR_NULL_POINTER; - } - - NS_RELEASE(uaURL); - } - - return rv; -} - -NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsEditor::DumpContentTree() -{ - nsCOMPtr thedoc; - nsCOMPtr presShell; - if (NS_SUCCEEDED(GetPresShell(getter_AddRefs(presShell)))) - { - presShell->GetDocument(getter_AddRefs(thedoc)); - if (thedoc) { - nsIContent* root = thedoc->GetRootContent(); - if (nsnull != root) { - root->List(stdout); - NS_RELEASE(root); - } - } - } - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::AddEditActionListener(nsIEditActionListener *aListener) -{ - if (!aListener) - return NS_ERROR_NULL_POINTER; - - if (!mActionListeners) - { - mActionListeners = new nsVoidArray(); - - if (!mActionListeners) - return NS_ERROR_OUT_OF_MEMORY; - } - - if (!mActionListeners->AppendElement((void *)aListener)) - return NS_ERROR_FAILURE; - - NS_ADDREF(aListener); - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener) -{ - if (!aListener || !mActionListeners) - return NS_ERROR_FAILURE; - - if (!mActionListeners->RemoveElement((void *)aListener)) - return NS_ERROR_FAILURE; - - NS_IF_RELEASE(aListener); - - if (mActionListeners->Count() < 1) - { - delete mActionListeners; - mActionListeners = 0; - } - - return NS_OK; -} - - -NS_IMETHODIMP -nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener) -{ - if (!aListener) - return NS_ERROR_NULL_POINTER; - - nsresult rv = NS_OK; - - if (!mDocStateListeners) - { - rv = NS_NewISupportsArray(getter_AddRefs(mDocStateListeners)); - if (NS_FAILED(rv)) return rv; - } - - nsCOMPtr iSupports = do_QueryInterface(aListener, &rv); - if (NS_FAILED(rv)) return rv; - - // is it already in the list? - PRInt32 foundIndex; - if (NS_SUCCEEDED(mDocStateListeners->GetIndexOf(iSupports, &foundIndex)) && foundIndex != -1) - return NS_OK; - - return mDocStateListeners->AppendElement(iSupports); -} - - -NS_IMETHODIMP -nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener) -{ - if (!aListener || !mDocStateListeners) - return NS_ERROR_NULL_POINTER; - - nsresult rv; - nsCOMPtr iSupports = do_QueryInterface(aListener, &rv); - if (NS_FAILED(rv)) return rv; - - return mDocStateListeners->RemoveElement(iSupports); -} NS_IMETHODIMP nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType) @@ -1710,270 +1762,6 @@ nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationTyp return rv; } -NS_IMETHODIMP -nsEditor::GetDocumentModified(PRBool *outDocModified) -{ - if (!outDocModified) - return NS_ERROR_NULL_POINTER; - - nsCOMPtr theDoc; - nsresult rv = GetDocument(getter_AddRefs(theDoc)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr diskDoc = do_QueryInterface(theDoc, &rv); - if (NS_FAILED(rv)) return rv; - - PRInt32 modCount = 0; - diskDoc->GetModCount(&modCount); - - *outDocModified = (modCount != 0); - return NS_OK; -} - -nsString & nsIEditor::GetTextNodeTag() -{ - static nsString gTextNodeTag("special text node tag"); - return gTextNodeTag; -} - -NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, - nsIDOMNode * aParent, - PRInt32 aPosition, - nsIDOMNode ** aNewNode) -{ - CreateElementTxn *txn; - nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition, &txn); - if (NS_SUCCEEDED(result)) - { - result = Do(txn); - if (NS_SUCCEEDED(result)) - { - result = txn->GetNewNode(aNewNode); - NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::Do succeeded."); - } - } - return result; -} - -NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsString& aTag, - nsIDOMNode *aParent, - PRInt32 aPosition, - CreateElementTxn ** aTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (nsnull != aParent) - { - result = TransactionFactory::GetNewTransaction(CreateElementTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(this, aTag, aParent, aPosition); - } - } - return result; -} - -NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, - nsIDOMNode * aParent, - PRInt32 aPosition) -{ - PRInt32 i; - nsIEditActionListener *listener; - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->WillInsertNode(aNode, aParent, aPosition); - } - } - - InsertElementTxn *txn; - nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); - } - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->DidInsertNode(aNode, aParent, aPosition, result); - } - } - - return result; -} - -NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode, - nsIDOMNode * aParent, - PRInt32 aPosition, - InsertElementTxn ** aTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (aNode && aParent && aTxn) - { - result = TransactionFactory::GetNewTransaction(InsertElementTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(aNode, aParent, aPosition, this); - } - } - return result; -} - -NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) -{ - PRInt32 i; - nsIEditActionListener *listener; - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->WillDeleteNode(aElement); - } - } - - DeleteElementTxn *txn; - nsresult result = CreateTxnForDeleteElement(aElement, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); - } - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->DidDeleteNode(aElement, result); - } - } - - return result; -} - -NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement, - DeleteElementTxn ** aTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (nsnull != aElement) - { - result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(aElement); - } - } - return result; -} - -NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (aAggTxn) - { - *aAggTxn = nsnull; - result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn**)aAggTxn); - - if (NS_FAILED(result) || !*aAggTxn) { - return NS_ERROR_OUT_OF_MEMORY; - } - - // Set the name for the aggregate transaction - (*aAggTxn)->SetName(aTxnName); - - // Get current selection and setup txn to delete it, - // but only if selection exists (is not a collapsed "caret" state) - nsCOMPtr selection; - result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) - { - PRBool collapsed; - result = selection->GetIsCollapsed(&collapsed); - if (NS_SUCCEEDED(result) && !collapsed) { - EditAggregateTxn *delSelTxn; - result = CreateTxnForDeleteSelection(nsIEditor::eDoNothing, - &delSelTxn); - if (NS_SUCCEEDED(result) && delSelTxn) { - (*aAggTxn)->AppendChild(delSelTxn); - } - } - } - } - return result; -} - - -NS_IMETHODIMP -nsEditor::InsertText(const nsString& aStringToInsert) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertText(aStringToInsert); - -#endif // ENABLE_JS_EDITOR_LOG - - EditAggregateTxn *aggTxn = nsnull; - // Create the "delete current selection" txn - nsresult result = CreateAggregateTxnForDeleteSelection(InsertTextTxn::gInsertTextTxnName, &aggTxn); - if ((NS_FAILED(result)) || (nsnull==aggTxn)) { - return NS_ERROR_OUT_OF_MEMORY; - } - InsertTextTxn *txn; - result = CreateTxnForInsertText(aStringToInsert, nsnull, &txn); // insert at the current selection - if ((NS_SUCCEEDED(result)) && txn) { - BeginUpdateViewBatch(); - aggTxn->AppendChild(txn); - result = Do(aggTxn); - EndUpdateViewBatch(); - } - else if (NS_ERROR_EDITOR_NO_SELECTION==result) { - result = DoInitialInsert(aStringToInsert); - } - else if (NS_ERROR_EDITOR_NO_TEXTNODE==result) - { - BeginTransaction(); - result = Do(aggTxn); - if (NS_SUCCEEDED(result)) - { - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - nsCOMPtr selectedNode; - PRInt32 offset; - result = selection->GetAnchorNode(getter_AddRefs(selectedNode)); - if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offset)) && selectedNode) - { - nsCOMPtr newNode; - result = CreateNode(GetTextNodeTag(), selectedNode, offset, - getter_AddRefs(newNode)); - if (NS_SUCCEEDED(result) && newNode) - { - nsCOMPtrnewTextNode; - newTextNode = do_QueryInterface(newNode); - if (newTextNode) - { - nsAutoString placeholderText(" "); - newTextNode->SetData(placeholderText); - selection->Collapse(newNode, 0); - selection->Extend(newNode, 1); - result = InsertText(aStringToInsert); - } - } - } - } - } - EndTransaction(); - } - return result; -} NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert, nsIDOMCharacterData *aTextNode, @@ -2155,460 +1943,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData *aElement, } -NS_IMETHODIMP nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag, - nsIDOMNode ** aNewNode) -{ - nsCOMPtr parentSelectedNode; - PRInt32 offsetOfNewNode; - nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, - offsetOfNewNode); - if (!NS_SUCCEEDED(result)) - return result; - nsCOMPtr newNode; - result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode, - getter_AddRefs(newNode)); - - *aNewNode = newNode; - - // we want the selection to be just after the new node - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - selection->Collapse(parentSelectedNode, offsetOfNewNode+1); - - return result; -} - -NS_IMETHODIMP nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode) -{ - nsresult result=NS_ERROR_NOT_INITIALIZED; - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - PRBool collapsed; - result = selection->GetIsCollapsed(&collapsed); - if (NS_SUCCEEDED(result) && !collapsed) - { - result = DeleteSelection(nsIEditor::eDoNothing); - if (NS_FAILED(result)) { - return result; - } - // get the new selection - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) { - return result; - } -#ifdef NS_DEBUG - nsCOMPtrtestSelectedNode; - nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode)); - // no selection is ok. - // if there is a selection, it must be collapsed - if (testSelectedNode) - { - PRBool testCollapsed; - debugResult = selection->GetIsCollapsed(&testCollapsed); - NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); - NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion"); - } -#endif - } - // split the selected node - PRInt32 offsetOfSelectedNode; - result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); - if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode) - { - nsCOMPtr selectedNode; - PRUint32 selectedNodeContentCount=0; - nsCOMPtrselectedParentNodeAsText; - selectedParentNodeAsText = do_QueryInterface(parentSelectedNode); - - /* if the selection is a text node, split the text node if necesary - and compute where to put the new node - */ - if (selectedParentNodeAsText) - { - PRInt32 indexOfTextNodeInParent; - selectedNode = do_QueryInterface(parentSelectedNode); - selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode)); - selectedParentNodeAsText->GetLength(&selectedNodeContentCount); - GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent); - - if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) - { - nsCOMPtr newSiblingNode; - result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); - // now get the node's offset in it's parent, and insert the new tag there - if (NS_SUCCEEDED(result)) { - result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - } - } - else - { // determine where to insert the new node - if (0==offsetOfSelectedNode) { - offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent - } - else { // insert new node as last child - GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node - } - } - } - /* if the selection is not a text node, split the parent node if necesary - and compute where to put the new node - */ - else - { // it's an interior node - nsCOMPtrparentChildList; - parentSelectedNode->GetChildNodes(getter_AddRefs(parentChildList)); - if ((NS_SUCCEEDED(result)) && parentChildList) - { - result = parentChildList->Item(offsetOfSelectedNode, getter_AddRefs(selectedNode)); - if ((NS_SUCCEEDED(result)) && selectedNode) - { - nsCOMPtrselectedNodeAsText; - selectedNodeAsText = do_QueryInterface(selectedNode); - nsCOMPtrchildList; - //CM: I added "result =" - result = selectedNode->GetChildNodes(getter_AddRefs(childList)); - if (NS_SUCCEEDED(result)) - { - if (childList) - { - childList->GetLength(&selectedNodeContentCount); - } - else - { - // This is the case where the collapsed selection offset - // points to an inline node with no children - // This must also be where the new node should be inserted - // and there is no splitting necessary - offsetOfNewNode = offsetOfSelectedNode; - return NS_OK; - } - } - else - { - return NS_ERROR_FAILURE; - } - if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) - { - nsCOMPtr newSiblingNode; - result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); - // now get the node's offset in it's parent, and insert the new tag there - if (NS_SUCCEEDED(result)) { - result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - } - } - else - { // determine where to insert the new node - if (0==offsetOfSelectedNode) { - offsetOfNewNode = 0; // insert new node as first child - } - else { // insert new node as last child - GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node - } - } - } - } - } - - // Here's where the new node was inserted - } - else { - printf("InsertBreak into an empty document is not yet supported\n"); - } - } - return result; -} - -NS_IMETHODIMP -nsEditor::DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->DeleteSelection(aAction); -#endif // ENABLE_JS_EDITOR_LOG - - nsresult result; - - EditAggregateTxn *txn; - result = CreateTxnForDeleteSelection(aAction, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); - } - - return result; -} - -NS_IMETHODIMP nsEditor::CreateTxnForDeleteSelection(nsIEditor::ECollapsedSelectionAction aAction, - EditAggregateTxn ** aTxn) -{ - if (!aTxn) - return NS_ERROR_NULL_POINTER; - *aTxn = nsnull; - - nsresult result; - nsCOMPtr selection; - result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - // Check whether the selection is collapsed and we should do nothing: - PRBool isCollapsed; - result = (selection->GetIsCollapsed(&isCollapsed)); - if (NS_SUCCEEDED(result) && isCollapsed && aAction == eDoNothing) - return NS_OK; - - // allocate the out-param transaction - result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn **)aTxn); - if (NS_FAILED(result)) { - return result; - } - - nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(result) && enumerator) - { - for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next()) - { - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(result)) && (currentItem)) - { - nsCOMPtr range( do_QueryInterface(currentItem) ); - range->GetIsCollapsed(&isCollapsed); - if (PR_FALSE==isCollapsed) - { - DeleteRangeTxn *txn; - result = TransactionFactory::GetNewTransaction(DeleteRangeTxn::GetCID(), (EditTxn **)&txn); - if ((NS_SUCCEEDED(result)) && (nsnull!=txn)) - { - txn->Init(this, range); - (*aTxn)->AppendChild(txn); - } - else - result = NS_ERROR_OUT_OF_MEMORY; - } - else - { // we have an insertion point. delete the thing in front of it or behind it, depending on aAction - result = CreateTxnForDeleteInsertionPoint(range, aAction, *aTxn); - } - } - } - } - } - - // if we didn't build the transaction correctly, destroy the out-param transaction so we don't leak it. - if (NS_FAILED(result)) - { - NS_IF_RELEASE(*aTxn); - } - - return result; -} - - -//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented -NS_IMETHODIMP -nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, - nsIEditor::ECollapsedSelectionAction - aAction, - EditAggregateTxn *aTxn) -{ - nsCOMPtr node; - PRBool isFirst; - PRBool isLast; - PRInt32 offset; - //PRInt32 length=1; - - // get the node and offset of the insertion point - nsresult result = aRange->GetStartParent(getter_AddRefs(node)); - if (NS_FAILED(result)) - return result; - result = aRange->GetStartOffset(&offset); - if (NS_FAILED(result)) - return result; - - // determine if the insertion point is at the beginning, middle, or end of the node - nsCOMPtr nodeAsText; - nsCOMPtr selectedNode; - nodeAsText = do_QueryInterface(node); - - if (nodeAsText) - { - PRUint32 count; - nodeAsText->GetLength(&count); - isFirst = PRBool(0==offset); - isLast = PRBool(count==(PRUint32)offset); - } - else - { - // get the child list and count - nsCOMPtrchildList; - PRUint32 count=0; - result = node->GetChildNodes(getter_AddRefs(childList)); - if ((NS_SUCCEEDED(result)) && childList) - { - childList->GetLength(&count); - childList->Item(offset, getter_AddRefs(selectedNode)); - } - isFirst = PRBool(0==offset); - isLast = PRBool((count-1)==(PRUint32)offset); - } -// XXX: if isFirst && isLast, then we'll need to delete the node - // as well as the 1 child - - // build a transaction for deleting the appropriate data - // XXX: this has to come from rule section - if ((nsIEditor::eDeleteLeft==aAction) && (PR_TRUE==isFirst)) - { // we're backspacing from the beginning of the node. Delete the first thing to our left - nsCOMPtr priorNode; - result = GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode)); - if ((NS_SUCCEEDED(result)) && priorNode) - { // there is a priorNode, so delete it's last child (if text content, delete the last char.) - // if it has no children, delete it - nsCOMPtr priorNodeAsText; - priorNodeAsText = do_QueryInterface(priorNode); - if (priorNodeAsText) - { - PRUint32 length=0; - priorNodeAsText->GetLength(&length); - if (0AppendChild(txn); - } - } - else - { // XXX: can you have an empty text node? If so, what do you do? - printf("ERROR: found a text node with 0 characters\n"); - result = NS_ERROR_UNEXPECTED; - } - } - else - { // priorNode is not text, so tell it's parent to delete it - DeleteElementTxn *txn; - result = CreateTxnForDeleteElement(priorNode, &txn); - if (NS_SUCCEEDED(result)) { - aTxn->AppendChild(txn); - } - } - } - } - else if ((nsIEditor::eDeleteRight==aAction) && (PR_TRUE==isLast)) - { // we're deleting from the end of the node. Delete the first thing to our right - nsCOMPtr nextNode; - result = GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode)); - if ((NS_SUCCEEDED(result)) && nextNode) - { // there is a priorNode, so delete it's last child (if text content, delete the last char.) - // if it has no children, delete it - nsCOMPtr nextNodeAsText; - nextNodeAsText = do_QueryInterface(nextNode); - if (nextNodeAsText) - { - PRUint32 length=0; - nextNodeAsText->GetLength(&length); - if (0AppendChild(txn); - } - } - else - { // XXX: can you have an empty text node? If so, what do you do? - printf("ERROR: found a text node with 0 characters\n"); - result = NS_ERROR_UNEXPECTED; - } - } - else - { // nextNode is not text, so tell it's parent to delete it - DeleteElementTxn *txn; - result = CreateTxnForDeleteElement(nextNode, &txn); - if (NS_SUCCEEDED(result)) { - aTxn->AppendChild(txn); - } - } - } - } - else - { - if (nodeAsText) - { // we have text, so delete a char at the proper offset - if (nsIEditor::eDeleteLeft==aAction) { - offset --; - } - DeleteTextTxn *txn; - result = CreateTxnForDeleteText(nodeAsText, offset, 1, &txn); - if (NS_SUCCEEDED(result)) { - aTxn->AppendChild(txn); - } - } - else - { // we're deleting a node - DeleteElementTxn *txn; - result = CreateTxnForDeleteElement(selectedNode, &txn); - if (NS_SUCCEEDED(result)) { - aTxn->AppendChild(txn); - } - } - } - return result; -} - - -NS_IMETHODIMP -nsEditor::SplitNode(nsIDOMNode * aNode, - PRInt32 aOffset, - nsIDOMNode **aNewLeftNode) -{ - PRInt32 i; - nsIEditActionListener *listener; - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->WillSplitNode(aNode, aOffset); - } - } - - SplitElementTxn *txn; - nsresult result = CreateTxnForSplitNode(aNode, aOffset, &txn); - if (NS_SUCCEEDED(result)) - { - result = Do(txn); - if (NS_SUCCEEDED(result)) - { - result = txn->GetNewNode(aNewLeftNode); - NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode"); - } - } - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - { - nsIDOMNode *ptr = (aNewLeftNode) ? *aNewLeftNode : 0; - listener->DidSplitNode(aNode, aOffset, ptr, result); - } - } - } - - return result; -} NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode, PRUint32 aOffset, @@ -2625,43 +1960,6 @@ NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode, return result; } -NS_IMETHODIMP -nsEditor::JoinNodes(nsIDOMNode * aLeftNode, - nsIDOMNode * aRightNode, - nsIDOMNode * aParent) -{ - PRInt32 i; - nsIEditActionListener *listener; - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->WillJoinNodes(aLeftNode, aRightNode, aParent); - } - } - - JoinElementTxn *txn; - nsresult result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); - } - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->DidJoinNodes(aLeftNode, aRightNode, aParent, result); - } - } - - return result; -} - NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, JoinElementTxn **aTxn) @@ -2680,6 +1978,12 @@ NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, // END nsEditor core implementation +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsEditor public static helper methods --- +#pragma mark - +#endif + // BEGIN nsEditor public static helper methods nsresult @@ -2849,6 +2153,7 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, return result; } + nsresult nsEditor::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset) { @@ -3615,213 +2920,6 @@ void nsEditor::HACKForceRedraw() #endif } - -NS_IMETHODIMP nsEditor::GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject) -{ - nsresult result = NS_ERROR_FAILURE; // we return an error unless we get the index - if( mPresShell != nsnull ) - { - if ((nsnull!=aNode)) - { // get the content interface - nsCOMPtr nodeAsContent( do_QueryInterface(aNode) ); - if (nodeAsContent) - { // get the frame from the content interface - //Note: frames are not ref counted, so don't use an nsCOMPtr - *aLayoutObject = nsnull; - result = mPresShell->GetLayoutObjectFor(nodeAsContent, aLayoutObject); - } - } - else { - result = NS_ERROR_NULL_POINTER; - } - } - return result; -} - -// -// The BeingComposition method is called from the Editor Composition event listeners. -// It caches the current text node and offset which is subsequently used for the -// created of IMETextTxn's. -// -NS_IMETHODIMP -nsEditor::BeginComposition(void) -{ -#ifdef DEBUG_tague - printf("nsEditor::StartComposition\n"); -#endif - nsresult result; - PRInt32 offset; - nsCOMPtr selection; - nsCOMPtr nodeAsText; - - result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - result = NS_ERROR_UNEXPECTED; - nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(result) && enumerator) - { - enumerator->First(); - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(result)) && (currentItem)) - { - result = NS_ERROR_UNEXPECTED; - nsCOMPtr range(do_QueryInterface(currentItem)); - if (range) - { - nsCOMPtr node; - result = range->GetStartParent(getter_AddRefs(node)); - if ((NS_SUCCEEDED(result)) && (node)) - { - nodeAsText = do_QueryInterface(node); - range->GetStartOffset(&offset); - if (!nodeAsText) { - result = NS_ERROR_EDITOR_NO_TEXTNODE; - } - } - } - } - else - { - result = NS_ERROR_EDITOR_NO_SELECTION; - } - } - } - - if (NS_SUCCEEDED(result) && nodeAsText) - { - // - // store the information needed to construct IME transactions for this composition - // - mIMETextNode = nodeAsText; - mIMETextOffset = offset; - mIMEBufferLength = 0; - } - - return result; -} - -NS_IMETHODIMP -nsEditor::EndComposition(void) -{ - nsresult result; - IMECommitTxn *commitTxn; - - // - // create the commit transaction..we can do it directly from the transaction mgr - // - result = TransactionFactory::GetNewTransaction(IMECommitTxn::GetCID(), (EditTxn**)&commitTxn); - if (NS_SUCCEEDED(result) && commitTxn!=nsnull) - { - commitTxn->Init(); - result = Do(commitTxn); - } - - /* reset the data we need to construct a transaction */ - mIMETextNode = do_QueryInterface(nsnull); - mIMETextOffset = 0; - mIMEBufferLength = 0; - - return result; -} - -NS_IMETHODIMP -nsEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply) -{ - nsCOMPtr caretP; - nsresult result = SetInputMethodText(aCompositionString,aTextRangeList); - mIMEBufferLength = aCompositionString.Length(); - - mPresShell->GetCaret(getter_AddRefs(caretP)); - caretP->GetWindowRelativeCoordinates(aReply->mCursorPosition,aReply->mCursorIsCollapsed); - - return result; -} - -NS_IMETHODIMP -nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) -{ - NS_NOTREACHED("This should never get called. Overridden by subclasses"); - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::StartLogging(nsIFileSpec *aLogFile) -{ -#ifdef ENABLE_JS_EDITOR_LOG - - mJSEditorLog = new nsJSEditorLog(this, aLogFile); - - if (!mJSEditorLog) - return NS_ERROR_OUT_OF_MEMORY; - - if (mTxnMgr) - { - mJSTxnLog = new nsJSTxnLog(mJSEditorLog); - - if (mJSTxnLog) - { - NS_ADDREF(mJSTxnLog); - mTxnMgr->AddListener(mJSTxnLog); - } - else - return NS_ERROR_OUT_OF_MEMORY; - } - -#endif // ENABLE_JS_EDITOR_LOG - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::StopLogging() -{ -#ifdef ENABLE_JS_EDITOR_LOG - - if (mTxnMgr && mJSTxnLog) - mTxnMgr->RemoveListener(mJSTxnLog); - - if (mJSTxnLog) - { - NS_RELEASE(mJSTxnLog); - mJSTxnLog = 0; - } - - if (mJSEditorLog) - { - delete mJSEditorLog; - mJSEditorLog = 0; - } - -#endif // ENABLE_JS_EDITOR_LOG - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::DebugDumpContent() const -{ - nsCOMPtrcontent; - nsCOMPtrnodeList; - nsAutoString bodyTag = "body"; - mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if (nodeList) - { - PRUint32 count; - nodeList->GetLength(&count); - NS_ASSERTION(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtrbodyNode; - nodeList->Item(0, getter_AddRefs(bodyNode)); - if (bodyNode) { - content = do_QueryInterface(bodyNode); - } - } - content->List(); - return NS_OK; -} - nsresult nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, @@ -3914,7 +3012,7 @@ nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode) //END nsEditor Private methods NS_IMETHODIMP -nsEditor::SetInputMethodText(const nsString& aStringToInsert,nsIPrivateTextRangeList *aTextRangeList) +nsEditor::SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRangeList *aTextRangeList) { IMETextTxn *txn; nsresult result; @@ -3964,58 +3062,9 @@ nsEditor::SetInputMethodText(const nsString& aStringToInsert,nsIPrivateTextRange return result; } -NS_IMETHODIMP -nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert, - nsIPrivateTextRangeList* aTextRangeList, - IMETextTxn ** aTxn) -{ - nsresult result; - - if (mIMETextNode==nsnull) - BeginComposition(); - - result = TransactionFactory::GetNewTransaction(IMETextTxn::GetCID(), (EditTxn **)aTxn); - if (nsnull!=*aTxn) { - result = (*aTxn)->Init(mIMETextNode,mIMETextOffset,mIMEBufferLength,aTextRangeList,aStringToInsert,mPresShell); - } - else { - result = NS_ERROR_OUT_OF_MEMORY; - } - return result; -} -NS_IMETHODIMP -nsEditor::CreateTxnForAddStyleSheet(nsICSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn) -{ - nsresult rv = TransactionFactory::GetNewTransaction(AddStyleSheetTxn::GetCID(), (EditTxn **)aTxn); - if (NS_FAILED(rv)) - return rv; - - if (! *aTxn) - return NS_ERROR_OUT_OF_MEMORY; - - return (*aTxn)->Init(this, aSheet); -} - - - -NS_IMETHODIMP -nsEditor::CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn) -{ - nsresult rv = TransactionFactory::GetNewTransaction(RemoveStyleSheetTxn::GetCID(), (EditTxn **)aTxn); - if (NS_FAILED(rv)) - return rv; - - if (! *aTxn) - return NS_ERROR_OUT_OF_MEMORY; - - return (*aTxn)->Init(this, aSheet); -} - - - -NS_IMETHODIMP nsEditor::DoInitialInputMethodInsert(const nsString & aStringToInsert,nsIPrivateTextRangeList* aTextRangeList) +NS_IMETHODIMP nsEditor::DoInitialInputMethodInsert(const nsString & aStringToInsert, nsIPrivateTextRangeList* aTextRangeList) { if (!mDoc) { return NS_ERROR_NOT_INITIALIZED; @@ -4734,40 +3783,481 @@ nsresult nsEditor::EndUpdateViewBatch() return NS_OK; } -#if 0 -nsresult nsEditor::OpenDialog(const nsString &url) -{ - // Get the content window as the parent for the dialog - //nsWebShellWindow that lets you retrieve this. GetContentWebShell -} + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- protected nsEditor methods --- +#pragma mark - #endif -/****************************************************************************** - * nsAutoSelectionReset - *****************************************************************************/ -nsAutoSelectionReset::nsAutoSelectionReset(nsIDOMSelection *aSel) -{ - mInitialized = PR_FALSE; - mSel = do_QueryInterface(aSel); - if (mSel) - { - mSel->GetAnchorNode(getter_AddRefs(mStartNode)); - mSel->GetAnchorOffset(&mStartOffset); - mSel->GetFocusNode(getter_AddRefs(mEndNode)); - mSel->GetFocusOffset(&mEndOffset); - if (mStartNode && mEndNode) - mInitialized = PR_TRUE; - } -} - -nsAutoSelectionReset::~nsAutoSelectionReset() +NS_IMETHODIMP +nsEditor::DeleteSelectionImpl(ESelectionCollapseDirection aAction) { - if (mSel && mInitialized) - { - // restore original selection - mSel->Collapse(mStartNode, mStartOffset); - mSel->Extend(mEndNode, mEndOffset); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->DeleteSelection(aAction); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult result; + + EditAggregateTxn *txn; + result = CreateTxnForDeleteSelection(aAction, &txn); + if (NS_SUCCEEDED(result)) { + result = Do(txn); } + + return result; +} + + +/* Non-interface, protected methods */ + +NS_IMETHODIMP +nsEditor::DoAfterDoTransaction(nsITransaction *aTxn) +{ + nsresult rv = NS_OK; + + PRBool isTransientTransaction; + rv = aTxn->GetIsTransient(&isTransientTransaction); + if (NS_FAILED(rv)) + return rv; + + if (!isTransientTransaction) + { + // we need to deal here with the case where the user saved after some + // edits, then undid one or more times. Then, the undo count is -ve, + // but we can't let a do take it back to zero. So we flip it up to + // a +ve number. + PRInt32 modCount; + GetDocModCount(modCount); + if (modCount < 0) + modCount = -modCount; + + rv = IncDocModCount(1); // don't count transient transactions + } + + return rv; +} + + +NS_IMETHODIMP +nsEditor::DoAfterUndoTransaction() +{ + nsresult rv = NS_OK; + + rv = IncDocModCount(-1); // all undoable transactions are non-transient + + return rv; +} + +NS_IMETHODIMP +nsEditor::DoAfterRedoTransaction() +{ + nsresult rv = NS_OK; + + rv = IncDocModCount(1); // all redoable transactions are non-transient + + return rv; +} + +NS_IMETHODIMP +nsEditor::DoAfterDocumentSave() +{ + // the mod count is reset by nsIDiskDocument. + NotifyDocumentListeners(eDocumentStateChanged); + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement, + const nsString& aAttribute, + const nsString& aValue, + ChangeAttributeTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (nsnull != aElement) + { + result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) { + result = (*aTxn)->Init(this, aElement, aAttribute, aValue, PR_FALSE); + } + } + return result; +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, + const nsString& aAttribute, + ChangeAttributeTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (nsnull != aElement) + { + result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) + { + nsAutoString value; + result = (*aTxn)->Init(this, aElement, aAttribute, value, PR_TRUE); + } + } + return result; +} + + +NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsString& aTag, + nsIDOMNode *aParent, + PRInt32 aPosition, + CreateElementTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (nsnull != aParent) + { + result = TransactionFactory::GetNewTransaction(CreateElementTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) { + result = (*aTxn)->Init(this, aTag, aParent, aPosition); + } + } + return result; +} + + +NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode, + nsIDOMNode * aParent, + PRInt32 aPosition, + InsertElementTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (aNode && aParent && aTxn) + { + result = TransactionFactory::GetNewTransaction(InsertElementTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) { + result = (*aTxn)->Init(aNode, aParent, aPosition, this); + } + } + return result; +} + +NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement, + DeleteElementTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (nsnull != aElement) + { + result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) { + result = (*aTxn)->Init(aElement); + } + } + return result; +} + +NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (aAggTxn) + { + *aAggTxn = nsnull; + result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn**)aAggTxn); + + if (NS_FAILED(result) || !*aAggTxn) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // Set the name for the aggregate transaction + (*aAggTxn)->SetName(aTxnName); + + // Get current selection and setup txn to delete it, + // but only if selection exists (is not a collapsed "caret" state) + nsCOMPtr selection; + result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + PRBool collapsed; + result = selection->GetIsCollapsed(&collapsed); + if (NS_SUCCEEDED(result) && !collapsed) { + EditAggregateTxn *delSelTxn; + result = CreateTxnForDeleteSelection(eDoNothing, &delSelTxn); + if (NS_SUCCEEDED(result) && delSelTxn) { + (*aAggTxn)->AppendChild(delSelTxn); + } + } + } + } + return result; +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert, + nsIPrivateTextRangeList* aTextRangeList, + IMETextTxn ** aTxn) +{ + nsresult result; + + if (mIMETextNode==nsnull) + BeginComposition(); + + result = TransactionFactory::GetNewTransaction(IMETextTxn::GetCID(), (EditTxn **)aTxn); + if (nsnull!=*aTxn) { + result = (*aTxn)->Init(mIMETextNode,mIMETextOffset,mIMEBufferLength,aTextRangeList,aStringToInsert,mPresShell); + } + else { + result = NS_ERROR_OUT_OF_MEMORY; + } + return result; +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForAddStyleSheet(nsICSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn) +{ + nsresult rv = TransactionFactory::GetNewTransaction(AddStyleSheetTxn::GetCID(), (EditTxn **)aTxn); + if (NS_FAILED(rv)) + return rv; + + if (! *aTxn) + return NS_ERROR_OUT_OF_MEMORY; + + return (*aTxn)->Init(this, aSheet); +} + + + +NS_IMETHODIMP +nsEditor::CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn) +{ + nsresult rv = TransactionFactory::GetNewTransaction(RemoveStyleSheetTxn::GetCID(), (EditTxn **)aTxn); + if (NS_FAILED(rv)) + return rv; + + if (! *aTxn) + return NS_ERROR_OUT_OF_MEMORY; + + return (*aTxn)->Init(this, aSheet); +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAction, + EditAggregateTxn ** aTxn) +{ + if (!aTxn) + return NS_ERROR_NULL_POINTER; + *aTxn = nsnull; + + nsresult result; + nsCOMPtr selection; + result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + // Check whether the selection is collapsed and we should do nothing: + PRBool isCollapsed; + result = (selection->GetIsCollapsed(&isCollapsed)); + if (NS_SUCCEEDED(result) && isCollapsed && aAction == eDoNothing) + return NS_OK; + + // allocate the out-param transaction + result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn **)aTxn); + if (NS_FAILED(result)) { + return result; + } + + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next()) + { + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + range->GetIsCollapsed(&isCollapsed); + if (PR_FALSE==isCollapsed) + { + DeleteRangeTxn *txn; + result = TransactionFactory::GetNewTransaction(DeleteRangeTxn::GetCID(), (EditTxn **)&txn); + if ((NS_SUCCEEDED(result)) && (nsnull!=txn)) + { + txn->Init(this, range); + (*aTxn)->AppendChild(txn); + } + else + result = NS_ERROR_OUT_OF_MEMORY; + } + else + { // we have an insertion point. delete the thing in front of it or behind it, depending on aAction + result = CreateTxnForDeleteInsertionPoint(range, aAction, *aTxn); + } + } + } + } + } + + // if we didn't build the transaction correctly, destroy the out-param transaction so we don't leak it. + if (NS_FAILED(result)) + { + NS_IF_RELEASE(*aTxn); + } + + return result; +} + + +//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented +NS_IMETHODIMP +nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, + nsIEditor::ESelectionCollapseDirection + aAction, + EditAggregateTxn *aTxn) +{ + nsCOMPtr node; + PRBool isFirst; + PRBool isLast; + PRInt32 offset; + //PRInt32 length=1; + + // get the node and offset of the insertion point + nsresult result = aRange->GetStartParent(getter_AddRefs(node)); + if (NS_FAILED(result)) + return result; + result = aRange->GetStartOffset(&offset); + if (NS_FAILED(result)) + return result; + + // determine if the insertion point is at the beginning, middle, or end of the node + nsCOMPtr nodeAsText; + nsCOMPtr selectedNode; + nodeAsText = do_QueryInterface(node); + + if (nodeAsText) + { + PRUint32 count; + nodeAsText->GetLength(&count); + isFirst = PRBool(0==offset); + isLast = PRBool(count==(PRUint32)offset); + } + else + { + // get the child list and count + nsCOMPtrchildList; + PRUint32 count=0; + result = node->GetChildNodes(getter_AddRefs(childList)); + if ((NS_SUCCEEDED(result)) && childList) + { + childList->GetLength(&count); + childList->Item(offset, getter_AddRefs(selectedNode)); + } + isFirst = PRBool(0==offset); + isLast = PRBool((count-1)==(PRUint32)offset); + } +// XXX: if isFirst && isLast, then we'll need to delete the node + // as well as the 1 child + + // build a transaction for deleting the appropriate data + // XXX: this has to come from rule section + if ((eDeletePrevious==aAction) && (PR_TRUE==isFirst)) + { // we're backspacing from the beginning of the node. Delete the first thing to our left + nsCOMPtr priorNode; + result = GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode)); + if ((NS_SUCCEEDED(result)) && priorNode) + { // there is a priorNode, so delete it's last child (if text content, delete the last char.) + // if it has no children, delete it + nsCOMPtr priorNodeAsText; + priorNodeAsText = do_QueryInterface(priorNode); + if (priorNodeAsText) + { + PRUint32 length=0; + priorNodeAsText->GetLength(&length); + if (0AppendChild(txn); + } + } + else + { // XXX: can you have an empty text node? If so, what do you do? + printf("ERROR: found a text node with 0 characters\n"); + result = NS_ERROR_UNEXPECTED; + } + } + else + { // priorNode is not text, so tell it's parent to delete it + DeleteElementTxn *txn; + result = CreateTxnForDeleteElement(priorNode, &txn); + if (NS_SUCCEEDED(result)) { + aTxn->AppendChild(txn); + } + } + } + } + else if ((nsIEditor::eDeleteNext==aAction) && (PR_TRUE==isLast)) + { // we're deleting from the end of the node. Delete the first thing to our right + nsCOMPtr nextNode; + result = GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode)); + if ((NS_SUCCEEDED(result)) && nextNode) + { // there is a priorNode, so delete it's last child (if text content, delete the last char.) + // if it has no children, delete it + nsCOMPtr nextNodeAsText; + nextNodeAsText = do_QueryInterface(nextNode); + if (nextNodeAsText) + { + PRUint32 length=0; + nextNodeAsText->GetLength(&length); + if (0AppendChild(txn); + } + } + else + { // XXX: can you have an empty text node? If so, what do you do? + printf("ERROR: found a text node with 0 characters\n"); + result = NS_ERROR_UNEXPECTED; + } + } + else + { // nextNode is not text, so tell it's parent to delete it + DeleteElementTxn *txn; + result = CreateTxnForDeleteElement(nextNode, &txn); + if (NS_SUCCEEDED(result)) { + aTxn->AppendChild(txn); + } + } + } + } + else + { + if (nodeAsText) + { // we have text, so delete a char at the proper offset + if (nsIEditor::eDeletePrevious==aAction) { + offset --; + } + DeleteTextTxn *txn; + result = CreateTxnForDeleteText(nodeAsText, offset, 1, &txn); + if (NS_SUCCEEDED(result)) { + aTxn->AppendChild(txn); + } + } + else + { // we're deleting a node + DeleteElementTxn *txn; + result = CreateTxnForDeleteElement(selectedNode, &txn); + if (NS_SUCCEEDED(result)) { + aTxn->AppendChild(txn); + } + } + } + return result; } diff --git a/editor/base/nsEditor.h b/editor/base/nsEditor.h index 5cfb343a4b56..ca32802372d5 100644 --- a/editor/base/nsEditor.h +++ b/editor/base/nsEditor.h @@ -19,15 +19,20 @@ #ifndef __editor_h__ #define __editor_h__ +#include "nsCOMPtr.h" #include "prmon.h" + #include "nsIEditor.h" +#include "nsIEditorIMESupport.h" +#include "nsIEditorLogging.h" + #include "nsIDOMDocument.h" #include "nsIDOMSelection.h" #include "nsIDOMCharacterData.h" #include "nsIDOMEventListener.h" #include "nsIDOMRange.h" #include "nsIPrivateTextRange.h" -#include "nsCOMPtr.h" + #include "nsIStringBundle.h" #include "nsITransactionManager.h" #include "TransactionFactory.h" @@ -78,43 +83,10 @@ PRMonitor *GetEditorMonitor(); * manager, event interfaces. the idea for the event interfaces is to have them * delegate the actual commands to the editor independent of the XPFE implementation. */ -class nsEditor : public nsIEditor +class nsEditor : public nsIEditor, + public nsIEditorIMESupport, + public nsIEditorLogging { -private: - nsIPresShell *mPresShell; - nsIViewManager *mViewManager; - PRUint32 mUpdateCount; - nsCOMPtr mTxnMgr; - nsCOMPtr mEditProperty; - nsCOMPtr mLastStyleSheet; // is owning this dangerous? - - // - // data necessary to build IME transactions - // - nsCOMPtr mIMETextNode; - PRUint32 mIMETextOffset; - PRUint32 mIMEBufferLength; - - friend PRBool NSCanUnload(nsISupports* serviceMgr); - static PRInt32 gInstanceCount; - - nsVoidArray* mActionListeners; - nsCOMPtr mDocStateListeners; - nsCOMPtr mStringBundle; - - PRInt8 mDocDirtyState; // -1 = not initialized - -protected: - nsIDOMDocument * mDoc; - nsCOMPtr mDTD; - // Services are not nsCOMPtr friendly - nsIPref* mPrefs; - -#ifdef ENABLE_JS_EDITOR_LOG - nsJSEditorLog *mJSEditorLog; - nsJSTxnLog *mJSTxnLog; -#endif // ENABLE_JS_EDITOR_LOG - public: enum IterDirection @@ -134,77 +106,67 @@ public: * for someone to derive from the nsEditor later? I dont believe so. */ virtual ~nsEditor(); - -/*BEGIN nsIEditor for more details*/ //Interfaces for addref and release and queryinterface //NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsEditor NS_DECL_ISUPPORTS - NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell); - + /* ------------ nsIEditor methods -------------- */ + NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, PRUint32 aFlags); NS_IMETHOD PostCreate(); - + NS_IMETHOD GetFlags(PRUint32 *aFlags) = 0; + NS_IMETHOD SetFlags(PRUint32 aFlags) = 0; NS_IMETHOD GetDocument(nsIDOMDocument **aDoc); - - NS_IMETHOD GetBodyElement(nsIDOMElement **aElement); - NS_IMETHOD GetPresShell(nsIPresShell **aPS); - NS_IMETHOD GetSelection(nsIDOMSelection **aSelection); + + NS_IMETHOD EnableUndo(PRBool aEnable); + NS_IMETHOD Do(nsITransaction *aTxn); + NS_IMETHOD Undo(PRUint32 aCount); + NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo); + NS_IMETHOD Redo(PRUint32 aCount); + NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo); - NS_IMETHOD SetProperties(nsVoidArray *aPropList); + NS_IMETHOD BeginTransaction(); + NS_IMETHOD EndTransaction(); - NS_IMETHOD GetProperties(nsVoidArray *aPropList); + // file handling + NS_IMETHOD Save(); + NS_IMETHOD SaveAs(PRBool aSavingCopy); + NS_IMETHOD GetDocumentModified(PRBool *outDocModified); + // these are pure virtual in this base class + NS_IMETHOD Cut() = 0; + NS_IMETHOD Copy() = 0; + NS_IMETHOD Paste() = 0; + + NS_IMETHOD SelectAll(); + + NS_IMETHOD BeginningOfDocument(); + NS_IMETHOD EndOfDocument(); + + + /* Node and element manipulation */ NS_IMETHOD SetAttribute(nsIDOMElement * aElement, const nsString& aAttribute, const nsString& aValue); - + NS_IMETHOD GetAttributeValue(nsIDOMElement * aElement, const nsString& aAttribute, nsString& aResultValue, PRBool& aResultIsSet); - + NS_IMETHOD RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute); - //NOTE: Most callers are dealing with Nodes, - // but these objects must supports nsIDOMElement - NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); - NS_IMETHOD CreateNode(const nsString& aTag, nsIDOMNode * aParent, PRInt32 aPosition, nsIDOMNode ** aNewNode); - + NS_IMETHOD InsertNode(nsIDOMNode * aNode, nsIDOMNode * aParent, PRInt32 aPosition); - NS_IMETHOD InsertText(const nsString& aStringToInsert); - - NS_IMETHOD BeginComposition(void); - - NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList, nsTextEventReply *aReply); - - NS_IMETHOD EndComposition(void); - - NS_IMETHOD OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags); - NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags); - NS_IMETHOD DumpContentTree(); - - NS_IMETHOD DeleteNode(nsIDOMNode * aChild); - - NS_IMETHOD DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction); - - NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode); - - NS_IMETHOD SplitNode(nsIDOMNode * aExistingRightNode, PRInt32 aOffset, nsIDOMNode ** aNewLeftNode); @@ -213,69 +175,63 @@ public: nsIDOMNode * aRightNode, nsIDOMNode * aParent); - NS_IMETHOD InsertBreak(); + NS_IMETHOD DeleteNode(nsIDOMNode * aChild); - NS_IMETHOD EnableUndo(PRBool aEnable); - NS_IMETHOD Do(nsITransaction *aTxn); - - NS_IMETHOD Undo(PRUint32 aCount); - - NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo); - - NS_IMETHOD Redo(PRUint32 aCount); - - NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo); - - NS_IMETHOD BeginTransaction(); - - NS_IMETHOD EndTransaction(); - - NS_IMETHOD GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject); - - NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin); - - NS_IMETHOD SelectAll(); - - NS_IMETHOD BeginningOfDocument(); - - NS_IMETHOD EndOfDocument(); - - NS_IMETHOD Cut(); - - NS_IMETHOD Copy(); - - NS_IMETHOD Paste(); - - NS_IMETHOD PasteAsQuotation(); - - NS_IMETHOD InsertAsQuotation(const nsString& aQuotedText); - - NS_IMETHOD ApplyStyleSheet(const nsString& aURL); - NS_IMETHOD AddStyleSheet(nsICSSStyleSheet* aSheet); - NS_IMETHOD RemoveStyleSheet(nsICSSStyleSheet* aSheet); + /* output */ + NS_IMETHOD OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags); + + NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags); + /* Listeners */ NS_IMETHOD AddEditActionListener(nsIEditActionListener *aListener); - NS_IMETHOD RemoveEditActionListener(nsIEditActionListener *aListener); NS_IMETHOD AddDocumentStateListener(nsIDocumentStateListener *aListener); NS_IMETHOD RemoveDocumentStateListener(nsIDocumentStateListener *aListener); - NS_IMETHOD GetDocumentModified(PRBool *outDocModified); + + NS_IMETHOD DumpContentTree(); + NS_IMETHOD DebugDumpContent() const; NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); + /* ------------ nsIEditorIMESupport methods -------------- */ + + NS_IMETHOD BeginComposition(void); + NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply); + NS_IMETHOD EndComposition(void); + + /* ------------ nsIEditorLogging methods -------------- */ + NS_IMETHOD StartLogging(nsIFileSpec *aLogFile); NS_IMETHOD StopLogging(); -/*END nsIEditor interfaces*/ +public: - /* StyleSheet load callback */ - static void ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData); + NS_IMETHOD InsertTextImpl(const nsString& aStringToInsert); + NS_IMETHOD DeleteSelectionImpl(ESelectionCollapseDirection aAction); + -/*BEGIN private methods used by the implementations of the above functions*/ protected: + + + // why not use the one in nsHTMLDocument? + NS_IMETHOD GetBodyElement(nsIDOMElement **aElement); + + //NOTE: Most callers are dealing with Nodes, + // but these objects must supports nsIDOMElement + NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); + /* + NS_IMETHOD SetProperties(nsVoidArray *aPropList); + NS_IMETHOD GetProperties(nsVoidArray *aPropList); + */ + /** create a transaction for setting aAttribute to aValue on aElement */ NS_IMETHOD CreateTxnForSetAttribute(nsIDOMElement *aElement, @@ -308,6 +264,26 @@ protected: NS_IMETHOD CreateTxnForDeleteElement(nsIDOMNode * aElement, DeleteElementTxn ** aTxn); + + /** Create an aggregate transaction for deleting current selection + * Used by all methods that need to delete current selection, + * then insert something new to replace it + * @param nsString& aTxnName is the name of the aggregated transaction + * @param EditTxn **aAggTxn is the return location of the aggregate TXN, + * with the DeleteSelectionTxn as the first child ONLY + * if there was a selection to delete. + */ + NS_IMETHOD CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn); + + + NS_IMETHOD CreateTxnForDeleteSelection(ESelectionCollapseDirection aAction, + EditAggregateTxn ** aTxn); + + NS_IMETHOD CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, + ESelectionCollapseDirection aAction, + EditAggregateTxn *aTxn); + + /** create a transaction for inserting aStringToInsert into aTextNode * if aTextNode is null, the string is inserted at the current selection. */ @@ -326,15 +302,12 @@ protected: /** create a transaction for removing a style sheet */ NS_IMETHOD CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn); - - /* remove the old style sheet, and apply the supplied one */ - NS_IMETHOD ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet); /** insert aStringToInsert as the first text in the document */ NS_IMETHOD DoInitialInsert(const nsString & aStringToInsert); - NS_IMETHOD DoInitialInputMethodInsert(const nsString& aStringToInsert,nsIPrivateTextRangeList* aTextRangeList); + NS_IMETHOD DoInitialInputMethodInsert(const nsString & aStringToInsert, nsIPrivateTextRangeList* aTextRangeList); NS_IMETHOD DeleteText(nsIDOMCharacterData *aElement, @@ -345,14 +318,7 @@ protected: PRUint32 aOffset, PRUint32 aLength, DeleteTextTxn **aTxn); - - NS_IMETHOD CreateTxnForDeleteSelection(nsIEditor::ECollapsedSelectionAction aAction, - EditAggregateTxn ** aTxn); - - NS_IMETHOD CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, - nsIEditor::ECollapsedSelectionAction aAction, - EditAggregateTxn *aTxn); - + NS_IMETHOD CreateTxnForSplitNode(nsIDOMNode *aNode, PRUint32 aOffset, SplitElementTxn **aTxn); @@ -361,19 +327,8 @@ protected: nsIDOMNode *aRightNode, JoinElementTxn **aTxn); - /** Create an aggregate transaction for deleting current selection - * Used by all methods that need to delete current selection, - * then insert something new to replace it - * @param nsString& aTxnName is the name of the aggregated transaction - * @param EditTxn **aAggTxn is the return location of the aggregate TXN, - * with the DeleteSelectionTxn as the first child ONLY - * if there was a selection to delete. - */ - NS_IMETHOD CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn); - NS_IMETHOD DebugDumpContent() const; - - NS_IMETHOD SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRangeList* aTextRangeList); + NS_IMETHOD SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRangeList *aTextRangeList); // called each time we modify the document. Increments the mod // count of the doc. @@ -415,9 +370,16 @@ protected: // document after a change via the DOM - gpk 2/13/99 void HACKForceRedraw(void); - NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode); +// file handling utils + + NS_IMETHOD SaveDocument(PRBool saveAs, PRBool saveCopy); + + NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin); public: + + static nsString& GetTextNodeTag(); + /** * SplitNode() creates a new node identical to an existing node, and split the contents between the two nodes * @param aExistingRightNode the node to split. It will become the new node's next sibling. @@ -626,39 +588,45 @@ public: nsresult BeginUpdateViewBatch(void); nsresult EndUpdateViewBatch(void); -}; -class nsAutoEditBatch -{ - private: - nsCOMPtr mEd; - public: - nsAutoEditBatch( nsIEditor *aEd) : mEd(do_QueryInterface(aEd)) - { if (mEd) mEd->BeginTransaction(); } - ~nsAutoEditBatch() { if (mEd) mEd->EndTransaction(); } -}; +protected: -class nsAutoSelectionReset -{ - private: - /** ref-counted reference to the selection that we are supposed to restore */ - nsCOMPtr mSel; + PRUint32 mFlags; // behavior flags. See nsIHTMLEditor.h for the flags we use. + + nsIPresShell *mPresShell; + nsIViewManager *mViewManager; + PRUint32 mUpdateCount; + nsCOMPtr mTxnMgr; + nsCOMPtr mEditProperty; + nsCOMPtr mLastStyleSheet; // is owning this dangerous? - /** PR_TRUE if this instance initialized itself correctly */ - PRBool mInitialized; + // + // data necessary to build IME transactions + // + nsCOMPtr mIMETextNode; + PRUint32 mIMETextOffset; + PRUint32 mIMEBufferLength; - nsCOMPtr mStartNode; - nsCOMPtr mEndNode; - PRInt32 mStartOffset; - PRInt32 mEndOffset; + nsVoidArray* mActionListeners; + nsCOMPtr mDocStateListeners; + nsCOMPtr mStringBundle; - public: - /** constructor responsible for remembering all state needed to restore aSel */ - nsAutoSelectionReset(nsIDOMSelection *aSel); - - /** destructor restores mSel to its former state */ - ~nsAutoSelectionReset(); + PRInt8 mDocDirtyState; // -1 = not initialized + + nsIDOMDocument * mDoc; + nsCOMPtr mDTD; + // Services are not nsCOMPtr friendly + nsIPref* mPrefs; + +#ifdef ENABLE_JS_EDITOR_LOG + nsJSEditorLog *mJSEditorLog; + nsJSTxnLog *mJSTxnLog; +#endif // ENABLE_JS_EDITOR_LOG + + static PRInt32 gInstanceCount; + + friend PRBool NSCanUnload(nsISupports* serviceMgr); }; diff --git a/editor/base/nsEditorEventListeners.cpp b/editor/base/nsEditorEventListeners.cpp index 70675019803d..7ac517663e53 100644 --- a/editor/base/nsEditorEventListeners.cpp +++ b/editor/base/nsEditorEventListeners.cpp @@ -17,6 +17,9 @@ */ #include "nsEditorEventListeners.h" #include "nsEditor.h" +#include "nsVoidArray.h" +#include "nsString.h" + #include "nsIDOMDocument.h" #include "nsIDocument.h" #include "nsIPresShell.h" @@ -25,16 +28,11 @@ #include "nsIDOMCharacterData.h" #include "nsIEditProperty.h" #include "nsISupportsArray.h" -#include "nsVoidArray.h" -#include "nsString.h" #include "nsIStringStream.h" #include "nsIDOMUIEvent.h" #include "nsIDOMNSUIEvent.h" #include "nsIPrivateTextEvent.h" - -// for testing only -#include "nsIHTMLEditor.h" -// end for testing only +#include "nsIEditorMailSupport.h" // for repainting hack only #include "nsIView.h" @@ -142,7 +140,7 @@ nsTextEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) // break; case nsIDOMUIEvent::VK_DELETE: - mEditor->DeleteSelection(nsIEditor::eDeleteRight); + mEditor->DeleteSelection(nsIEditor::eDeleteNext); break; // case nsIDOMUIEvent::VK_RETURN: @@ -181,7 +179,7 @@ nsTextEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) { PRUint32 flags=0; mEditor->GetFlags(&flags); - if (! (flags & TEXT_EDITOR_FLAG_SINGLELINE)) + if (! (flags & nsIHTMLEditor::eEditorSingleLineMask)) { PRBool ctrlKey, altKey, metaKey; uiEvent->GetCtrlKey(&ctrlKey); @@ -192,7 +190,10 @@ nsTextEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) // else we insert the tab straight through nsAutoString key; key += keyCode; - mEditor->InsertText(key); + + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) + htmlEditor->InsertText(key); return NS_ERROR_BASE; // this means "I handled the event, don't do default processing" } else { @@ -244,14 +245,17 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) if (metaKey) return NS_OK; // don't consume + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (!htmlEditor) return NS_ERROR_NO_INTERFACE; + if (NS_SUCCEEDED(uiEvent->GetKeyCode(&keyCode))) { if (nsIDOMUIEvent::VK_BACK==keyCode) { - mEditor->DeleteSelection(nsIEditor::eDeleteLeft); + mEditor->DeleteSelection(nsIEditor::eDeletePrevious); return NS_ERROR_BASE; // consumed } if (nsIDOMUIEvent::VK_RETURN==keyCode) { - mEditor->InsertBreak(); + htmlEditor->InsertBreak(); return NS_ERROR_BASE; // consumed } } @@ -263,7 +267,7 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) return NS_OK; // ignore tabs here, they're handled in keyDown if at all } key += character; - mEditor->InsertText(key); + htmlEditor->InsertText(key); } return NS_ERROR_BASE; // consumed @@ -333,28 +337,6 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr format = "text/html"; res = mEditor->OutputToString(output, format, nsEditor::EditorOutputFormatted); -#if 0 - nsCOMPtr htmlEditor (do_QueryInterface(mEditor)); - if (htmlEditor) - { - if (isShift) - res = htmlEditor->OutputTextToString(output, PR_TRUE, PR_FALSE); - else - res = htmlEditor->OutputHTMLToString(output, PR_FALSE); - } - else - { - nsCOMPtr textEditor (do_QueryInterface(mEditor)); - if (textEditor) - { - if (isShift) - res = textEditor->OutputTextToString(output, PR_TRUE, PR_FALSE); - else - res = textEditor->OutputHTMLToString(output, PR_FALSE); - } - } -#endif - if (NS_SUCCEEDED(res)) { char* buf = output.ToNewCString(); @@ -378,11 +360,15 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr { printf("Getting number of columns\n"); aProcessed=PR_TRUE; - PRInt32 wrap; - if (NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - printf("Currently wrapping to %d\n", wrap); - else - printf("GetBodyWrapWidth returned an error\n"); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + { + PRInt32 wrap; + if (NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + printf("Currently wrapping to %d\n", wrap); + else + printf("GetBodyWrapWidth returned an error\n"); + } } break; @@ -390,20 +376,24 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr // hard coded "Decrease wrap size" if (PR_TRUE==altKey) { - aProcessed=PR_TRUE; - PRInt32 wrap; - if (!NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - { - printf("GetBodyWrapWidth returned an error\n"); - break; - } - mEditor->SetBodyWrapWidth(wrap - 5); - if (!NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - { - printf("Second GetBodyWrapWidth returned an error\n"); - break; - } - else printf("Now wrapping to %d\n", wrap); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + { + aProcessed=PR_TRUE; + PRInt32 wrap; + if (!NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + { + printf("GetBodyWrapWidth returned an error\n"); + break; + } + mailEditor->SetBodyWrapWidth(wrap - 5); + if (!NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + { + printf("Second GetBodyWrapWidth returned an error\n"); + break; + } + else printf("Now wrapping to %d\n", wrap); + } } break; @@ -411,20 +401,24 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr // hard coded "Increase wrap size" if (PR_TRUE==altKey) { - aProcessed=PR_TRUE; - PRInt32 wrap; - if (!NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - { - printf("GetBodyWrapWidth returned an error\n"); - break; - } - mEditor->SetBodyWrapWidth(wrap + 5); - if (!NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - { - printf("Second GetBodyWrapWidth returned an error\n"); - break; - } - else printf("Now wrapping to %d\n", wrap); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + { + aProcessed=PR_TRUE; + PRInt32 wrap; + if (!NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + { + printf("GetBodyWrapWidth returned an error\n"); + break; + } + mailEditor->SetBodyWrapWidth(wrap + 5); + if (!NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + { + printf("Second GetBodyWrapWidth returned an error\n"); + break; + } + else printf("Now wrapping to %d\n", wrap); + } } break; @@ -436,7 +430,11 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr if (mEditor) { if (altKey) - mEditor->PasteAsQuotation(); + { + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + mailEditor->PasteAsQuotation(); + } else mEditor->Paste(); } @@ -465,19 +463,20 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_I: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) PRBool any = PR_FALSE; PRBool all = PR_FALSE; PRBool first = PR_FALSE; - mEditor->GetTextProperty(nsIEditProperty::i, nsnull, nsnull, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::i, nsnull, nsnull, first, any, all); if (PR_FALSE==first) { - mEditor->SetTextProperty(nsIEditProperty::i, nsnull, nsnull); + htmlEditor->SetInlineProperty(nsIEditProperty::i, nsnull, nsnull); } else { - mEditor->RemoveTextProperty(nsIEditProperty::i, nsnull); + htmlEditor->RemoveInlineProperty(nsIEditProperty::i, nsnull); } } } @@ -486,7 +485,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr else if (PR_TRUE==altKey) { aProcessed=PR_TRUE; - nsCOMPtr htmlEditor (do_QueryInterface(mEditor)); + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); if (htmlEditor) { nsString nsstr ("This is bold and emphasized text"); @@ -499,19 +498,20 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_B: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) PRBool any = PR_FALSE; PRBool all = PR_FALSE; PRBool first = PR_FALSE; - mEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); if (PR_FALSE==first) { - mEditor->SetTextProperty(nsIEditProperty::b, nsnull, nsnull); + htmlEditor->SetInlineProperty(nsIEditProperty::b, nsnull, nsnull); } else { - mEditor->RemoveTextProperty(nsIEditProperty::b, nsnull); + htmlEditor->RemoveInlineProperty(nsIEditProperty::b, nsnull); } } } @@ -521,19 +521,20 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_U: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) PRBool any = PR_FALSE; PRBool all = PR_FALSE; PRBool first = PR_FALSE; - mEditor->GetTextProperty(nsIEditProperty::u, nsnull, nsnull, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::u, nsnull, nsnull, first, any, all); if (PR_FALSE==first) { - mEditor->SetTextProperty(nsIEditProperty::u, nsnull, nsnull); + htmlEditor->SetInlineProperty(nsIEditProperty::u, nsnull, nsnull); } else { - mEditor->RemoveTextProperty(nsIEditProperty::u, nsnull); + htmlEditor->RemoveInlineProperty(nsIEditProperty::u, nsnull); } } @@ -544,7 +545,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_1: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -553,9 +555,9 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr PRBool first = PR_FALSE; nsAutoString color = "COLOR"; nsAutoString value = "red"; - mEditor->GetTextProperty(nsIEditProperty::font, &color, &value, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::font, &color, &value, first, any, all); if (!all) { - mEditor->SetTextProperty(nsIEditProperty::font, &color, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &color, &value); } else { printf("NOOP: all selected text is already red\n"); @@ -568,7 +570,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_2: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -576,9 +579,9 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr PRBool all = PR_FALSE; PRBool first = PR_FALSE; nsAutoString color = "COLOR"; - mEditor->GetTextProperty(nsIEditProperty::font, &color, nsnull, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::font, &color, nsnull, first, any, all); if (any) { - mEditor->RemoveTextProperty(nsIEditProperty::font, &color); + htmlEditor->RemoveInlineProperty(nsIEditProperty::font, &color); } else { printf("NOOP: no color set\n"); @@ -591,7 +594,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_3: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -600,7 +604,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr //PRBool first = PR_FALSE; nsAutoString prop = "SIZE"; nsAutoString value = "+2"; - mEditor->SetTextProperty(nsIEditProperty::font, &prop, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &prop, &value); } } break; @@ -609,7 +613,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_4: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -618,7 +623,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr //PRBool first = PR_FALSE; nsAutoString prop = "SIZE"; nsAutoString value = "-2"; - mEditor->SetTextProperty(nsIEditProperty::font, &prop, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &prop, &value); } } break; @@ -627,7 +632,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_5: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -636,7 +642,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr //PRBool first = PR_FALSE; nsAutoString prop = "FACE"; nsAutoString value = "helvetica"; - mEditor->SetTextProperty(nsIEditProperty::font, &prop, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &prop, &value); } } break; @@ -645,7 +651,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_6: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -654,7 +661,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr //PRBool first = PR_FALSE; nsAutoString prop = "FACE"; nsAutoString value = "times"; - mEditor->SetTextProperty(nsIEditProperty::font, &prop, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &prop, &value); } } break; @@ -663,16 +670,12 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_7: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tag; - nsIEditProperty::h1->ToString(tag); - htmlEditor->ReplaceBlockParent(tag); - } + nsAutoString tag; + nsIEditProperty::h1->ToString(tag); + htmlEditor->ReplaceBlockParent(tag); } } break; @@ -681,16 +684,12 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_8: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tag; - nsIEditProperty::h2->ToString(tag); - htmlEditor->ReplaceBlockParent(tag); - } + nsAutoString tag; + nsIEditProperty::h2->ToString(tag); + htmlEditor->ReplaceBlockParent(tag); } } break; @@ -699,13 +698,9 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_9: if (PR_TRUE==ctrlKey) { - if (mEditor) - { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) { - htmlEditor->RemoveParagraphStyle(); - } + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { + htmlEditor->RemoveParagraphStyle(); } } break; @@ -714,28 +709,23 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_0: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + printf("testing GetParagraphStyle\n"); + nsStringArray styles; + nsresult result = htmlEditor->GetParagraphStyle(&styles); + if (NS_SUCCEEDED(result)) { - printf("testing GetParagraphStyle\n"); - nsStringArray styles; - nsresult result = htmlEditor->GetParagraphStyle(&styles); - if (NS_SUCCEEDED(result)) + PRInt32 count = styles.Count(); + PRInt32 i; + for (i=0; iToNewCString(); - printf("%s ", tagCString); - delete [] tagCString; - } - printf("\n"); + nsString *tag = styles.StringAt(i); + char *tagCString = tag->ToNewCString(); + printf("%s ", tagCString); + delete [] tagCString; } + printf("\n"); } } } @@ -745,16 +735,12 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_COMMA: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tag; - nsIEditProperty::blockquote->ToString(tag); - htmlEditor->AddBlockParent(tag); - } + nsAutoString tag; + nsIEditProperty::blockquote->ToString(tag); + htmlEditor->AddBlockParent(tag); } } break; @@ -763,16 +749,12 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_PERIOD: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tag; - nsIEditProperty::blockquote->ToString(tag); - htmlEditor->RemoveParent(tag); - } + nsAutoString tag; + nsIEditProperty::blockquote->ToString(tag); + htmlEditor->RemoveParent(tag); } } break; @@ -907,8 +889,11 @@ nsTextEditorMouseListener::MouseDown(nsIDOMEvent* aMouseEvent) uiEvent->GetCtrlKey(&ctrlKey); if (ctrlKey) - return editor->PasteAsQuotation(); - + { + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + mailEditor->PasteAsQuotation(); + } return editor->Paste(); } @@ -933,24 +918,21 @@ nsTextEditorMouseListener::MouseClick(nsIDOMEvent* aMouseEvent) nsresult nsTextEditorMouseListener::MouseDblClick(nsIDOMEvent* aMouseEvent) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsIHTMLEditor * HTMLEditor; - if (NS_SUCCEEDED(mEditor->QueryInterface(nsIHTMLEditor::GetIID(), (void**)&HTMLEditor))) + nsCOMPtr selectedElement; + if (NS_SUCCEEDED(htmlEditor->GetSelectedElement("", getter_AddRefs(selectedElement))) && selectedElement) { - nsCOMPtr selectedElement; - if (NS_SUCCEEDED(HTMLEditor->GetSelectedElement("", getter_AddRefs(selectedElement))) && selectedElement) - { - nsAutoString TagName; - selectedElement->GetTagName(TagName); - TagName.ToLowerCase(); + nsAutoString TagName; + selectedElement->GetTagName(TagName); + TagName.ToLowerCase(); #if DEBUG_cmanske - char szTagName[64]; - TagName.ToCString(szTagName, 64); - printf("Single Selected element found: %s\n", szTagName); + char szTagName[64]; + TagName.ToCString(szTagName, 64); + printf("Single Selected element found: %s\n", szTagName); #endif - } } } return NS_OK; @@ -1035,7 +1017,7 @@ nsresult nsTextEditorTextListener::HandleText(nsIDOMEvent* aTextEvent) { nsString composedText; - nsresult result; + nsresult result = NS_OK; nsCOMPtr textEvent; nsIPrivateTextRangeList *textRangeList; nsTextEventReply *textEventReply; @@ -1050,7 +1032,9 @@ nsTextEditorTextListener::HandleText(nsIDOMEvent* aTextEvent) textEvent->GetInputRange(&textRangeList); textEvent->GetEventReply(&textEventReply); textRangeList->AddRef(); - result = mEditor->SetCompositionString(composedText,textRangeList,textEventReply); + nsCOMPtr imeEditor = do_QueryInterface(mEditor, &result); + if (imeEditor) + result = imeEditor->SetCompositionString(composedText,textRangeList,textEventReply); return result; } @@ -1205,9 +1189,12 @@ nsTextEditorDragListener::DragDrop(nsIDOMEvent* aMouseEvent) trans->GetAnyTransferData(&textMime, (void **)&str, &len); // If the string was not empty then paste it in - if (str) { + if (str) + { + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); stuffToPaste.SetString(str, len); - mEditor->InsertText(stuffToPaste); + if (htmlEditor) + htmlEditor->InsertText(stuffToPaste); dragSession->SetCanDrop(PR_TRUE); } @@ -1269,6 +1256,16 @@ nsTextEditorCompositionListener::HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; } + +void nsTextEditorCompositionListener::SetEditor(nsIEditor *aEditor) +{ + nsCOMPtr imeEditor = do_QueryInterface(aEditor); + if (!imeEditor) return; // should return an error here! + + // note that we don't hold an extra reference here. + mEditor = imeEditor; +} + nsresult nsTextEditorCompositionListener::HandleStartComposition(nsIDOMEvent* aCompositionEvent) { @@ -1291,7 +1288,7 @@ nsTextEditorCompositionListener::HandleEndComposition(nsIDOMEvent* aCompositionE nsresult NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, - nsITextEditor *aEditor) + nsIEditor *aEditor) { nsTextEditorKeyListener* it = new nsTextEditorKeyListener(); if (nsnull == it) { @@ -1307,7 +1304,7 @@ NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, nsresult NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, - nsITextEditor *aEditor) + nsIEditor *aEditor) { nsTextEditorMouseListener* it = new nsTextEditorMouseListener(); if (nsnull == it) { @@ -1321,7 +1318,7 @@ NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsresult -NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor* aEditor) +NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor* aEditor) { nsTextEditorTextListener* it = new nsTextEditorTextListener(); if (nsnull==it) { @@ -1337,7 +1334,7 @@ NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor nsresult NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, - nsITextEditor *aEditor) + nsIEditor *aEditor) { nsTextEditorDragListener* it = new nsTextEditorDragListener(); if (nsnull == it) { @@ -1350,7 +1347,7 @@ NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, } nsresult -NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor* aEditor) +NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor* aEditor) { nsTextEditorCompositionListener* it = new nsTextEditorCompositionListener(); if (nsnull==it) { @@ -1362,7 +1359,7 @@ NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsITex nsresult NS_NewEditorFocusListener(nsIDOMEventListener ** aInstancePtrResult, - nsITextEditor *aEditor) + nsIEditor *aEditor) { nsTextEditorFocusListener* it = new nsTextEditorFocusListener(); if (nsnull == it) { @@ -1428,12 +1425,15 @@ nsTextEditorFocusListener::HandleEvent(nsIDOMEvent* aEvent) nsresult nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) { + // turn on selection and caret + if (mEditor) + { // turn on selection and caret if (mEditor) { PRUint32 flags; mEditor->GetFlags(&flags); - if (! (flags & TEXT_EDITOR_FLAG_DISABLED)) + if (! (flags & nsIHTMLEditor::eEditorDisabledMask)) { // only enable caret and selection if the editor is not disabled nsCOMPtreditor = do_QueryInterface(mEditor); if (editor) @@ -1442,7 +1442,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) editor->GetPresShell(getter_AddRefs(ps)); if (ps) { - if (! (flags & TEXT_EDITOR_FLAG_READONLY)) + if (! (flags & nsIHTMLEditor::eEditorReadonlyMask)) { // only enable caret if the editor is not readonly ps->SetCaretEnabled(PR_TRUE); } @@ -1474,6 +1474,8 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) } return NS_OK; } + return NS_OK; +} nsresult nsTextEditorFocusListener::Blur(nsIDOMEvent* aEvent) diff --git a/editor/base/nsEditorEventListeners.h b/editor/base/nsEditorEventListeners.h index bef8a19e5601..a0d3b7f7947b 100644 --- a/editor/base/nsEditorEventListeners.h +++ b/editor/base/nsEditorEventListeners.h @@ -19,6 +19,9 @@ #ifndef editorInterfaces_h__ #define editorInterfaces_h__ +#include "nsCOMPtr.h" + + #include "nsIDOMEvent.h" #include "nsIDOMKeyListener.h" #include "nsIDOMMouseListener.h" @@ -26,8 +29,9 @@ #include "nsIDOMDragListener.h" #include "nsIDOMCompositionListener.h" #include "nsIDOMFocusListener.h" -#include "nsITextEditor.h" -#include "nsCOMPtr.h" + +#include "nsIEditor.h" +#include "nsIHTMLEditor.h" /** The nsTextEditorKeyListener public nsIDOMKeyListener * This class will delegate events to its editor according to the translation @@ -45,7 +49,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -63,7 +67,7 @@ protected: virtual nsresult ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProcessed); protected: - nsITextEditor* mEditor; // weak reference + nsIEditor* mEditor; // weak reference }; @@ -82,7 +86,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -94,11 +98,14 @@ public: /*END implementations of textevent handler interface*/ protected: - nsITextEditor* mEditor; // weak reference + nsIEditor* mEditor; // weak reference PRBool mCommitText; PRBool mInTransaction; }; + +class nsIEditorIMESupport; + class nsTextEditorCompositionListener : public nsIDOMCompositionListener { public: @@ -112,7 +119,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor); /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -125,7 +132,7 @@ public: /*END implementations of textevent handler interface*/ protected: - nsITextEditor* mEditor; // weak reference + nsIEditorIMESupport* mEditor; // weak reference }; @@ -144,7 +151,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -161,7 +168,7 @@ public: /*END implementations of mouseevent handler interface*/ protected: - nsITextEditor* mEditor; // weak reference + nsIEditor* mEditor; // weak reference }; @@ -181,7 +188,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -196,7 +203,7 @@ public: /*END implementations of dragevent handler interface*/ protected: - nsITextEditor* mEditor; + nsIEditor* mEditor; }; @@ -215,7 +222,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -228,34 +235,34 @@ public: /*END implementations of focus event handler interface*/ protected: - nsITextEditor* mEditor; // weak reference + nsIEditor* mEditor; // weak reference }; /** factory for the editor key listener */ -extern nsresult NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor mouse listener */ -extern nsresult NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor text listener */ -extern nsresult NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor drag listener */ -extern nsresult NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor composition listener */ -extern nsresult NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor composition listener */ -extern nsresult NS_NewEditorFocusListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorFocusListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor *aEditor); #endif //editorInterfaces_h__ diff --git a/editor/base/nsEditorShell.cpp b/editor/base/nsEditorShell.cpp index ee4483b41e09..9ca61c98ce84 100644 --- a/editor/base/nsEditorShell.cpp +++ b/editor/base/nsEditorShell.cpp @@ -73,8 +73,12 @@ #include "nsIDOMDocument.h" #include "nsIEditor.h" -#include "nsITextEditor.h" #include "nsIHTMLEditor.h" +#include "nsIEditorStyleSheets.h" +#include "nsIEditorMailSupport.h" +#include "nsITableEditor.h" +#include "nsIEditorLogging.h" + #include "nsEditorCID.h" #include "nsIComponentManager.h" @@ -94,9 +98,7 @@ /* Define Class IDs */ static NS_DEFINE_IID(kAppShellServiceCID, NS_APPSHELL_SERVICE_CID); -static NS_DEFINE_IID(kEditorAppCoreCID, NS_EDITORAPPCORE_CID); static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); -static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); static NS_DEFINE_CID(kCTextServicesDocumentCID, NS_TEXTSERVICESDOCUMENT_CID); static NS_DEFINE_CID(kCSpellCheckerCID, NS_SPELLCHECKER_CID); static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); @@ -317,54 +319,39 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell) nsresult err = NS_OK; - if (mEditorTypeString == "text") + nsCOMPtr editor; + err = nsComponentManager::CreateInstance(kHTMLEditorCID, nsnull, nsIEditor::GetIID(), getter_AddRefs(editor)); + if(!editor) + err = NS_ERROR_OUT_OF_MEMORY; + + if (NS_SUCCEEDED(err)) { - nsCOMPtr editor; - err = nsComponentManager::CreateInstance(kTextEditorCID, nsnull, nsITextEditor::GetIID(), getter_AddRefs(editor)); - if(!editor) - err = NS_ERROR_OUT_OF_MEMORY; - - if (NS_SUCCEEDED(err)) + if (mEditorTypeString == "text") { - err = editor->Init(aDoc, aPresShell); - if (NS_SUCCEEDED(err) && editor) - { - mEditor = do_QueryInterface(editor); // this does the addref that is the owning reference - mEditorType = ePlainTextEditorType; - - // and set the initial wrap column - editor->SetBodyWrapWidth(mWrapColumn); - } - } - } - else if (mEditorTypeString == "html" || mEditorTypeString == "") // empty string default to HTML editor - { - nsCOMPtr editor; - err = nsComponentManager::CreateInstance(kHTMLEditorCID, nsnull, nsIHTMLEditor::GetIID(), getter_AddRefs(editor)); - if(!editor) - err = NS_ERROR_OUT_OF_MEMORY; - - if (NS_SUCCEEDED(err)) + err = editor->Init(aDoc, aPresShell, nsIHTMLEditor::eEditorPlaintextMask); + } + else if (mEditorTypeString == "html" || mEditorTypeString == "") // empty string default to HTML editor { - err = editor->Init(aDoc, aPresShell); - if (NS_SUCCEEDED(err) && editor) - { - mEditor = do_QueryInterface(editor); // this does the addref that is the owning reference - mEditorType = eHTMLTextEditorType; - } - } - } - else - { - err = NS_ERROR_INVALID_ARG; // this is not an editor we know about + err = editor->Init(aDoc, aPresShell, 0); + } + else + { + err = NS_ERROR_INVALID_ARG; // this is not an editor we know about #if DEBUG - nsString errorMsg = "Failed to init editor. Unknown editor type \""; - errorMsg += mEditorTypeString; - errorMsg += "\"\n"; - char *errorMsgCString = errorMsg.ToNewCString(); - NS_WARNING(errorMsgCString); - delete [] errorMsgCString; + nsString errorMsg = "Failed to init editor. Unknown editor type \""; + errorMsg += mEditorTypeString; + errorMsg += "\"\n"; + char *errorMsgCString = errorMsg.ToNewCString(); + NS_WARNING(errorMsgCString); + delete [] errorMsgCString; #endif + } + + if (NS_SUCCEEDED(err) && editor) + { + mEditor = do_QueryInterface(editor); // this does the addref that is the owning reference + mEditorType = eHTMLTextEditorType; + } } return err; @@ -441,19 +428,9 @@ nsEditorShell::SetTextProperty(const PRUnichar *prop, const PRUnichar *attr, con switch (mEditorType) { case ePlainTextEditorType: - { // should we allow this? - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->SetTextProperty(styleAtom, &attributeStr, &valueStr); - } - break; case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->SetTextProperty(styleAtom, &attributeStr, &valueStr); - } + err = mEditor->SetInlineProperty(styleAtom, &attributeStr, &valueStr); break; default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -482,19 +459,9 @@ nsEditorShell::RemoveOneProperty(const nsString& aProp, const nsString &aAttr) switch (mEditorType) { case ePlainTextEditorType: - { // should we allow this? - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->RemoveTextProperty(styleAtom, &aAttr); - } - break; case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->RemoveTextProperty(styleAtom, &aAttr); - } + err = mEditor->RemoveInlineProperty(styleAtom, &aAttr); break; default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -566,19 +533,9 @@ nsEditorShell::GetTextProperty(const PRUnichar *prop, const PRUnichar *attr, con switch (mEditorType) { case ePlainTextEditorType: - { // should we allow this? - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->GetTextProperty(styleAtom, &aAttr, &aValue, *firstHas, *anyHas, *allHas); - } - break; case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->GetTextProperty(styleAtom, &aAttr, &aValue, *firstHas, *anyHas, *allHas); - } + err = mEditor->GetInlineProperty(styleAtom, &aAttr, &aValue, *firstHas, *anyHas, *allHas); break; default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -593,25 +550,7 @@ NS_IMETHODIMP nsEditorShell::SetBackgroundColor(const PRUnichar *color) nsAutoString aColor(color); - switch (mEditorType) - { - case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->SetBackgroundColor(aColor); - break; - } - case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - result = textEditor->SetBackgroundColor(aColor); - } - break; - default: - result = NS_ERROR_NOT_IMPLEMENTED; - } + result = mEditor->SetBackgroundColor(aColor); return result; } @@ -622,25 +561,9 @@ NS_IMETHODIMP nsEditorShell::ApplyStyleSheet(const PRUnichar *url) nsAutoString aURL(url); - switch (mEditorType) - { - case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - result = textEditor->ApplyStyleSheet(aURL); - } - break; - case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->ApplyStyleSheet(aURL); - } - break; - default: - result = NS_ERROR_NOT_IMPLEMENTED; - } + nsCOMPtr styleSheetFoobar = do_QueryInterface(mEditor); + if (styleSheetFoobar) + result = styleSheetFoobar->ApplyStyleSheet(aURL); return result; } @@ -656,9 +579,7 @@ NS_IMETHODIMP nsEditorShell::SetBodyAttribute(const PRUnichar *attr, const PRUni { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->SetBodyAttribute(aAttr, aValue); + result = mEditor->SetBodyAttribute(aAttr, aValue); break; } default: @@ -1028,36 +949,31 @@ static NS_DEFINE_IID(kCFileWidgetCID, NS_FILEWIDGET_CID); { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + // This was written for all local file getting + // TODO: NEED TO GET PROPER PARENT WINDOW + PRUnichar *fileURLString = nsnull; + nsAutoString filterType("html"); + result = GetLocalFileURL(nsnull, filterType.GetUnicode(), &fileURLString); + if (NS_FAILED(result) || !fileURLString || !*fileURLString) + return result; - if (htmlEditor) + // all I want to do is call a method on nsToolkitCore that would normally + // be static. But I have to go through all this crap. XPCOM sucks so bad. + static NS_DEFINE_IID(kToolkitCoreCID, NS_TOOLKITCORE_CID); + nsCOMPtr toolkitCore; + result = nsComponentManager::CreateInstance(kToolkitCoreCID, + nsnull, + nsIDOMToolkitCore::GetIID(), + getter_AddRefs(toolkitCore)); + if (NS_SUCCEEDED(result) && toolkitCore) { - // This was written for all local file getting - // TODO: NEED TO GET PROPER PARENT WINDOW - PRUnichar *fileURLString = nsnull; - nsAutoString filterType("html"); - result = GetLocalFileURL(nsnull, filterType.GetUnicode(), &fileURLString); - if (NS_FAILED(result) || !fileURLString || !*fileURLString) - return result; - - // all I want to do is call a method on nsToolkitCore that would normally - // be static. But I have to go through all this crap. XPCOM sucks so bad. - static NS_DEFINE_IID(kToolkitCoreCID, NS_TOOLKITCORE_CID); - nsCOMPtr toolkitCore; - result = nsComponentManager::CreateInstance(kToolkitCoreCID, - nsnull, - nsIDOMToolkitCore::GetIID(), - getter_AddRefs(toolkitCore)); - if (NS_SUCCEEDED(result) && toolkitCore) - { - // at some point we need to be passing nsFileSpecs around. When nsIURI is fileSpec- - // savvy, we should use that. - result = toolkitCore->ShowWindowWithArgs("chrome://editor/content", nsnull, fileURLString/*fileURL.GetAsString()*/); - } - - // delete the string - + // at some point we need to be passing nsFileSpecs around. When nsIURI is fileSpec- + // savvy, we should use that. + result = toolkitCore->ShowWindowWithArgs("chrome://editor/content", nsnull, fileURLString/*fileURL.GetAsString()*/); } + + // delete the string + break; } default: @@ -1076,19 +992,14 @@ nsEditorShell::Save() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Save(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Save(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Save(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1104,19 +1015,14 @@ nsEditorShell::SaveAs() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->SaveAs(PR_FALSE); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->SaveAs(PR_FALSE); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->SaveAs(PR_FALSE); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1243,19 +1149,14 @@ nsEditorShell::Undo() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Undo(1); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Undo(1); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Undo(1); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1271,19 +1172,14 @@ nsEditorShell::Redo() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Redo(1); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Redo(1); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Redo(1); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1299,19 +1195,14 @@ nsEditorShell::Cut() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Cut(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Cut(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Cut(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1327,19 +1218,14 @@ nsEditorShell::Copy() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Copy(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Copy(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Copy(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1355,19 +1241,14 @@ nsEditorShell::Paste() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Paste(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Paste(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Paste(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1383,19 +1264,14 @@ nsEditorShell::PasteAsQuotation() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->PasteAsQuotation(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->PasteAsQuotation(); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->PasteAsQuotation(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1413,19 +1289,14 @@ nsEditorShell::PasteAsCitedQuotation(const PRUnichar *cite) switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->PasteAsQuotation(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->PasteAsCitedQuotation(aCiteString); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->PasteAsQuotation(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1443,19 +1314,14 @@ nsEditorShell::InsertAsQuotation(const PRUnichar *quotedText) switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->InsertAsQuotation(aQuotedText); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->InsertAsQuotation(aQuotedText); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->InsertAsQuotation(aQuotedText); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1474,19 +1340,14 @@ nsEditorShell::InsertAsCitedQuotation(const PRUnichar *quotedText, const PRUnich switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->InsertAsQuotation(aQuotedText); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->InsertAsCitedQuotation(aQuotedText, aCiteString); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->InsertAsQuotation(aQuotedText); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1502,19 +1363,14 @@ nsEditorShell::SelectAll() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->SelectAll(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->SelectAll(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->SelectAll(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1527,15 +1383,15 @@ NS_IMETHODIMP nsEditorShell::DeleteSelection(PRInt32 action) { nsresult err = NS_NOINTERFACE; - nsIEditor::ECollapsedSelectionAction selectionAction; + nsIEditor::ESelectionCollapseDirection selectionAction; switch(action) { - case nsIEditor::eDeleteRight: - selectionAction = nsIEditor::eDeleteRight; + case 1: + selectionAction = nsIEditor::eDeleteNext; break; - case nsIEditor::eDeleteLeft: - selectionAction = nsIEditor::eDeleteLeft; + case 2: + selectionAction = nsIEditor::eDeletePrevious; break; default: selectionAction = nsIEditor::eDoNothing; @@ -1560,12 +1416,6 @@ nsEditorShell::InsertText(const PRUnichar *textToInsert) switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->InsertText(aTextToInsert); - } - break; case eHTMLTextEditorType: { nsCOMPtr htmlEditor = do_QueryInterface(mEditor); @@ -1573,6 +1423,7 @@ nsEditorShell::InsertText(const PRUnichar *textToInsert) err = htmlEditor->InsertText(aTextToInsert); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1590,12 +1441,6 @@ nsEditorShell::InsertSource(const PRUnichar *aSourceToInsert) switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->InsertText(sourceToInsert); - } - break; case eHTMLTextEditorType: { nsCOMPtr htmlEditor = do_QueryInterface(mEditor); @@ -1603,6 +1448,7 @@ nsEditorShell::InsertSource(const PRUnichar *aSourceToInsert) err = htmlEditor->InsertHTML(sourceToInsert); } break; + default: err = NS_NOINTERFACE; } @@ -1615,9 +1461,8 @@ nsEditorShell::InsertBreak() { nsresult err = NS_NOINTERFACE; - nsCOMPtr editor = do_QueryInterface(mEditor); - if (editor) - err = editor->InsertBreak(); + if (mEditor) + err = mEditor->InsertBreak(); return err; } @@ -1684,16 +1529,10 @@ nsEditorShell::GetContentsAs(const PRUnichar *format, PRUint32 flags, nsString aFormat (format); nsString aContentsAs; - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->OutputToString(aContentsAs, aFormat, flags); - else - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->OutputToString(aContentsAs, aFormat, flags); - } - + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->OutputToString(aContentsAs, aFormat, flags); + *contentsAs = aContentsAs.ToNewUnicode(); return err; @@ -1728,11 +1567,11 @@ nsEditorShell::GetWrapColumn(PRInt32* aWrapColumn) { case ePlainTextEditorType: { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) { PRInt32 wc; - err = textEditor->GetBodyWrapWidth(&wc); + err = mailEditor->GetBodyWrapWidth(&wc); if (NS_SUCCEEDED(err)) *aWrapColumn = (PRInt32)wc; } @@ -1758,9 +1597,9 @@ nsEditorShell::SetWrapColumn(PRInt32 aWrapColumn) { case ePlainTextEditorType: { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->SetBodyWrapWidth(mWrapColumn); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->SetBodyWrapWidth(mWrapColumn); } break; default: @@ -1781,11 +1620,7 @@ nsEditorShell::GetParagraphFormat(PRUnichar * *paragraphFormat) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->GetParagraphFormat(aParagraphFormat); - } + err = mEditor->GetParagraphFormat(aParagraphFormat); break; default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -1806,12 +1641,9 @@ nsEditorShell::SetParagraphFormat(PRUnichar * paragraphFormat) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->SetParagraphFormat(aParagraphFormat); - } + err = mEditor->SetParagraphFormat(aParagraphFormat); break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1871,12 +1703,9 @@ nsEditorShell::InsertList(const PRUnichar *listType) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->InsertList(aListType); - } + err = mEditor->InsertList(aListType); break; + case ePlainTextEditorType: default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -1895,12 +1724,9 @@ nsEditorShell::Indent(const PRUnichar *indent) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Indent(aIndent); - } + err = mEditor->Indent(aIndent); break; + case ePlainTextEditorType: default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -1919,12 +1745,9 @@ nsEditorShell::Align(const PRUnichar *align) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Align(aAlignType); - } + err = mEditor->Align(aAlignType); break; + case ePlainTextEditorType: default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -1934,24 +1757,20 @@ nsEditorShell::Align(const PRUnichar *align) } NS_IMETHODIMP -nsEditorShell::GetSelectedElement(const PRUnichar *tagName, nsIDOMElement **_retval) +nsEditorShell::GetSelectedElement(const PRUnichar *aInTagName, nsIDOMElement **aOutElement) { - if (!tagName || !_retval) + if (!aInTagName || !aOutElement) return NS_ERROR_NULL_POINTER; nsresult result = NS_NOINTERFACE; - nsAutoString TagName(tagName); + nsAutoString tagName(aInTagName); switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - nsCOMPtr element; - if (htmlEditor) - return htmlEditor->GetSelectedElement(TagName, _retval); - } + result = mEditor->GetSelectedElement(tagName, aOutElement); break; + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; @@ -1961,24 +1780,22 @@ nsEditorShell::GetSelectedElement(const PRUnichar *tagName, nsIDOMElement **_ret } NS_IMETHODIMP -nsEditorShell::GetElementOrParentByTagName(const PRUnichar *tagName, nsIDOMNode *node, nsIDOMElement **_retval) +nsEditorShell::GetElementOrParentByTagName(const PRUnichar *aInTagName, nsIDOMNode *node, nsIDOMElement **aOutElement) { //node can be null -- this signals using the selection anchorNode - if (!tagName || !_retval) + if (!aInTagName || !aOutElement) return NS_ERROR_NULL_POINTER; nsresult result = NS_NOINTERFACE; - nsAutoString TagName(tagName); + nsAutoString tagName(aInTagName); switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - return htmlEditor->GetElementOrParentByTagName(TagName, node, _retval); - } + result = mEditor->GetElementOrParentByTagName(tagName, node, aOutElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -1987,23 +1804,20 @@ nsEditorShell::GetElementOrParentByTagName(const PRUnichar *tagName, nsIDOMNode } NS_IMETHODIMP -nsEditorShell::CreateElementWithDefaults(const PRUnichar *tagName, nsIDOMElement **_retval) +nsEditorShell::CreateElementWithDefaults(const PRUnichar *aInTagName, nsIDOMElement **aOutElement) { - if (!_retval) + if (!aOutElement) return NS_ERROR_NULL_POINTER; nsresult result = NS_NOINTERFACE; - nsAutoString aTagName(tagName); + nsAutoString tagName(aInTagName); switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - return htmlEditor->CreateElementWithDefaults(aTagName, _retval); - } + result = mEditor->CreateElementWithDefaults(tagName, aOutElement); break; + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; @@ -2023,12 +1837,10 @@ nsEditorShell::InsertElement(nsIDOMElement *element, PRBool deleteSelection) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->InsertElement(element, deleteSelection); - } + result = mEditor->InsertElement(element, deleteSelection); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2043,12 +1855,11 @@ nsEditorShell::SaveHLineSettings(nsIDOMElement* aElement) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - return htmlEditor->SaveHLineSettings(aElement); - } + // this is bogus. We should save the HLine settings (or HRule, as it's more properly known) here. + result = mEditor->SaveHLineSettings(aElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2062,12 +1873,10 @@ nsEditorShell::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - return htmlEditor->InsertLinkAroundSelection(aAnchorElement); - } + result = mEditor->InsertLinkAroundSelection(aAnchorElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2084,12 +1893,10 @@ nsEditorShell::SelectElement(nsIDOMElement* aElement) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->SelectElement(aElement); - } + result = mEditor->SelectElement(aElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2107,12 +1914,10 @@ nsEditorShell::SetSelectionAfterElement(nsIDOMElement* aElement) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->SetCaretAfterElement(aElement); - } + result = mEditor->SetCaretAfterElement(aElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2130,11 +1935,13 @@ nsEditorShell::InsertTableCell(PRInt32 aNumber, PRBool bAfter) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->InsertTableCell(aNumber, bAfter); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->InsertTableCell(aNumber, bAfter); } break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2149,11 +1956,13 @@ nsEditorShell::InsertTableRow(PRInt32 aNumber, PRBool bAfter) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->InsertTableRow(aNumber,bAfter); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->InsertTableRow(aNumber,bAfter); } break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2168,9 +1977,9 @@ nsEditorShell::InsertTableColumn(PRInt32 aNumber, PRBool bAfter) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->InsertTableColumn(aNumber,bAfter); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->InsertTableColumn(aNumber,bAfter); } break; default: @@ -2187,9 +1996,9 @@ nsEditorShell::DeleteTable() { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->DeleteTable(); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->DeleteTable(); } break; default: @@ -2206,9 +2015,9 @@ nsEditorShell::DeleteTableCell(PRInt32 aNumber) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->DeleteTableCell(aNumber); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->DeleteTableCell(aNumber); } break; default: @@ -2226,9 +2035,9 @@ nsEditorShell::DeleteTableRow(PRInt32 aNumber) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->DeleteTableRow(aNumber); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->DeleteTableRow(aNumber); } break; default: @@ -2246,9 +2055,9 @@ nsEditorShell::DeleteTableColumn(PRInt32 aNumber) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->DeleteTableColumn(aNumber); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->DeleteTableColumn(aNumber); } break; default: @@ -2265,9 +2074,9 @@ nsEditorShell::JoinTableCells() { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->JoinTableCells(); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->JoinTableCells(); } break; default: @@ -2284,9 +2093,9 @@ nsEditorShell::NormalizeTable(nsIDOMElement *aTable) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->NormalizeTable(aTable); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->NormalizeTable(aTable); } break; default: @@ -2310,12 +2119,12 @@ nsEditorShell::GetRowIndex(nsIDOMElement *cellElement, PRInt32 *_retval) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) { // Get both row and column indexes - return just row PRInt32 colIndex; - result = htmlEditor->GetCellIndexes(cellElement, *_retval, colIndex); + result = tableEditor->GetCellIndexes(cellElement, *_retval, colIndex); } } break; @@ -2336,12 +2145,12 @@ nsEditorShell::GetColumnIndex(nsIDOMElement *cellElement, PRInt32 *_retval) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) { // Get both row and column indexes - return just column PRInt32 rowIndex; - result = htmlEditor->GetCellIndexes(cellElement, rowIndex, *_retval); + result = tableEditor->GetCellIndexes(cellElement, rowIndex, *_retval); } } break; @@ -2363,12 +2172,12 @@ nsEditorShell::GetTableRowCount(nsIDOMElement *tableElement, PRInt32 *_retval) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) { // This returns both the number of rows and columns: return just rows PRInt32 cols; - result = htmlEditor->GetTableSize(tableElement, *_retval, cols); + result = tableEditor->GetTableSize(tableElement, *_retval, cols); } } break; @@ -2390,12 +2199,12 @@ nsEditorShell::GetTableColumnCount(nsIDOMElement *tableElement, PRInt32 *_retval { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) { // This returns both the number of rows and columns: return just columns PRInt32 rows; - result = htmlEditor->GetTableSize(tableElement, rows, *_retval); + result = tableEditor->GetTableSize(tableElement, rows, *_retval); } } break; @@ -2417,9 +2226,9 @@ nsEditorShell::GetCellAt(nsIDOMElement *tableElement, PRInt32 rowIndex, PRInt32 { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->GetCellAt(tableElement, rowIndex, colIndex, *_retval); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetCellAt(tableElement, rowIndex, colIndex, *_retval); } break; default: @@ -2446,9 +2255,9 @@ nsEditorShell::GetCellDataAt(nsIDOMElement *tableElement, PRInt32 rowIndex, PRIn { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->GetCellDataAt(tableElement, rowIndex, colIndex, *_retval, + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetCellDataAt(tableElement, rowIndex, colIndex, *_retval, *aStartRowIndex, *aStartColIndex, *aRowSpan, *aColSpan, *aIsSelected); } break; @@ -2473,9 +2282,9 @@ nsEditorShell::GetEmbeddedObjects(nsISupportsArray **aObjectArray) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->GetEmbeddedObjects(aObjectArray); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + result = mailEditor->GetEmbeddedObjects(aObjectArray); } break; @@ -2714,19 +2523,14 @@ nsEditorShell::BeginBatchChanges() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->BeginTransaction(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->BeginTransaction(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->BeginTransaction(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -2742,19 +2546,14 @@ nsEditorShell::EndBatchChanges() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->EndTransaction(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->EndTransaction(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->EndTransaction(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -2822,30 +2621,21 @@ nsEditorShell::StartLogging(nsIFileSpec *logFile) { nsresult err = NS_OK; -#if 1 - switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->StartLogging(logFile); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->StartLogging(logFile); + nsCOMPtr logger = do_QueryInterface(mEditor); + if (logger) + err = logger->StartLogging(logFile); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } -#endif - return err; } @@ -2854,30 +2644,21 @@ nsEditorShell::StopLogging() { nsresult err = NS_OK; -#if 1 - switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->StopLogging(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->StopLogging(); + nsCOMPtr logger = do_QueryInterface(mEditor); + if (logger) + err = logger->StopLogging(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } -#endif - return err; } diff --git a/editor/base/nsEditorShell.h b/editor/base/nsEditorShell.h index 13bc408c1f68..42c87834742a 100644 --- a/editor/base/nsEditorShell.h +++ b/editor/base/nsEditorShell.h @@ -15,8 +15,8 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ -#ifndef nsEditorAppCore_h___ -#define nsEditorAppCore_h___ +#ifndef nsEditorShell_h___ +#define nsEditorShell_h___ //#include "nsAppCores.h" @@ -40,6 +40,7 @@ #include "nsIEditorSpellCheck.h" #include "nsISpellChecker.h" #include "nsInterfaceState.h" +#include "nsIHTMLEditor.h" class nsIBrowserWindow; class nsIWebShell; @@ -50,13 +51,11 @@ class nsIDOMNode; class nsIURI; class nsIWebShellWindow; class nsIPresShell; -class nsIHTMLEditor; -class nsITextEditor; class nsIOutputStream; class nsISupportsArray; -#define NS_EDITORAPPCORE_CID \ +#define NS_EDITORSHELL_CID \ { /* {} */ \ 0x9afff72b, 0xca9a, 0x11d2, \ { 0x96, 0xc9, 0x0, 0x60, 0xb0, 0xfb, 0x99, 0x56 } \ @@ -316,7 +315,7 @@ class nsEditorShell : public nsIEditorShell, EEditorType mEditorType; nsString mEditorTypeString; // string which describes which editor type will be instantiated (lowercased) - nsCOMPtr mEditor; // this can be either an HTML or plain text (or other?) editor + nsCOMPtr mEditor; // this can be either an HTML or plain text (or other?) editor nsCOMPtr mSearchContext; // context used for search and replace. Owned by the appshell. @@ -336,4 +335,4 @@ class nsEditorShell : public nsIEditorShell, nsCOMPtr mDocStateListeners; // contents are nsISupports }; -#endif // nsEditorAppCore_h___ +#endif // nsEditorShell_h___ diff --git a/editor/base/nsEditorShellFactory.cpp b/editor/base/nsEditorShellFactory.cpp index dc43913b1803..2e44ad5bdc4d 100644 --- a/editor/base/nsEditorShellFactory.cpp +++ b/editor/base/nsEditorShellFactory.cpp @@ -30,7 +30,7 @@ static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID); static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID); -static NS_DEFINE_IID(kEditorShellCID, NS_EDITORAPPCORE_CID); +static NS_DEFINE_IID(kEditorShellCID, NS_EDITORSHELL_CID); ///////////////////////////////////////////////////////////////////////// // nsEditorShellFactoryImpl diff --git a/editor/base/nsHTMLEditRules.cpp b/editor/base/nsHTMLEditRules.cpp index 20641a71e39f..35942e8f9574 100644 --- a/editor/base/nsHTMLEditRules.cpp +++ b/editor/base/nsHTMLEditRules.cpp @@ -17,11 +17,13 @@ */ #include "nsHTMLEditRules.h" + #include "nsEditor.h" #include "nsHTMLEditor.h" -#include "nsTextEditor.h" + #include "PlaceholderTxn.h" #include "InsertTextTxn.h" + #include "nsIContent.h" #include "nsIContentIterator.h" #include "nsIDOMNode.h" @@ -36,7 +38,7 @@ #include "nsIPresShell.h" #include "nsLayoutCID.h" -class nsIFrame; +#include "nsEditorUtils.h" //const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE"; //const static char* kMOZEditorBogusNodeValue="TRUE"; @@ -56,7 +58,8 @@ enum * Constructor/Destructor ********************************************************/ -nsHTMLEditRules::nsHTMLEditRules() +nsHTMLEditRules::nsHTMLEditRules(PRUint32 aFlags) +: nsTextEditRules(aFlags) { } @@ -285,7 +288,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel) nsresult -nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECollapsedSelectionAction aAction, PRBool *aCancel) +nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction, PRBool *aCancel) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param @@ -315,7 +318,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECo if (NS_FAILED(res)) return res; // at beginning of text node and backspaced? - if (!offset && (aAction == nsIEditor::eDeleteLeft)) + if (!offset && (aAction == nsIEditor::eDeletePrevious)) { nsCOMPtr priorNode; res = mEditor->GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode)); @@ -368,7 +371,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECo // at end of text node and deleted? if ((offset == (PRInt32)strLength) - && (aAction == nsIEditor::eDeleteRight)) + && (aAction == nsIEditor::eDeleteNext)) { nsCOMPtr nextNode; res = mEditor->GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode)); @@ -480,7 +483,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECo { // first delete the selection *aCancel = PR_TRUE; - res = mEditor->nsEditor::DeleteSelection(aAction); + res = mEditor->DeleteSelectionImpl(aAction); if (NS_FAILED(res)) return res; // then join para's, insert break res = mEditor->JoinNodeDeep(leftParent,rightParent,aSelection); @@ -493,7 +496,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECo { // first delete the selection *aCancel = PR_TRUE; - res = mEditor->nsEditor::DeleteSelection(aAction); + res = mEditor->DeleteSelectionImpl(aAction); if (NS_FAILED(res)) return res; // join blocks res = mEditor->JoinNodeDeep(leftParent,rightParent,aSelection); @@ -1284,7 +1287,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt } if (node) - offset++; // since this is going to be used for a range _endpoint_, we want to be after the node + offset++; // since this is going to be used for a range _endpoint_, we want to be after the node else node = parent; @@ -1859,11 +1862,3 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, } - - - - - - - - diff --git a/editor/base/nsHTMLEditRules.h b/editor/base/nsHTMLEditRules.h index a975859c3b96..e37c8ce57cc4 100644 --- a/editor/base/nsHTMLEditRules.h +++ b/editor/base/nsHTMLEditRules.h @@ -30,8 +30,8 @@ class nsHTMLEditRules : public nsTextEditRules { public: - nsHTMLEditRules(); - virtual ~nsHTMLEditRules(); + nsHTMLEditRules(PRUint32 aFlags); + virtual ~nsHTMLEditRules(); // nsEditRules methods NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel); @@ -61,7 +61,7 @@ protected: TypeInState typeInState, PRInt32 aMaxLength); nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel); - nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECollapsedSelectionAction aAction, PRBool *aCancel); + nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction, PRBool *aCancel); nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel); nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel); nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel); diff --git a/editor/base/nsHTMLEditor.cpp b/editor/base/nsHTMLEditor.cpp index 872746ba674f..5d9246a273a2 100644 --- a/editor/base/nsHTMLEditor.cpp +++ b/editor/base/nsHTMLEditor.cpp @@ -16,11 +16,12 @@ * Reserved. */ -#include "nsTextEditor.h" + #include "nsHTMLEditor.h" #include "nsHTMLEditRules.h" + #include "nsEditorEventListeners.h" -#include "nsInsertHTMLTxn.h" + #include "nsIDOMText.h" #include "nsIDOMNodeList.h" #include "nsIDOMDocument.h" @@ -31,6 +32,16 @@ #include "nsIDOMSelection.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMHTMLImageElement.h" + +#include "nsICSSLoader.h" +#include "nsICSSStyleSheet.h" +#include "nsIHTMLContentContainer.h" +#include "nsIStyleSet.h" +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIStyleContext.h" + #include "nsIEnumerator.h" #include "nsIContent.h" #include "nsIContentIterator.h" @@ -49,12 +60,27 @@ #include "nsIDOMDocumentFragment.h" #include "nsIPresShell.h" #include "nsIImage.h" +#include "nsAOLCiter.h" +#include "nsInternetCiter.h" + +// netwerk +#include "nsIURI.h" +#include "nsNeckoUtil.h" // Drag & Drop, Clipboard #include "nsWidgetsCID.h" #include "nsIClipboard.h" #include "nsITransferable.h" +// Transactionas +#include "PlaceholderTxn.h" +#include "nsStyleSheetTxns.h" +#include "nsInsertHTMLTxn.h" + +// Misc +#include "TextEditorTest.h" +#include "nsEditorUtils.h" + #include "prprf.h" const unsigned char nbsp = 160; @@ -69,12 +95,11 @@ const unsigned char nbsp = 160; { 0xa6cf9107, 0x15b3, 0x11d2, { 0x93, 0x2e, 0x0, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } } static NS_DEFINE_CID(kCNavDTDCID, NS_CNAVDTD_CID); -static NS_DEFINE_CID(kEditorCID, NS_EDITOR_CID); -static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); // Drag & Drop, Clipboard Support static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); @@ -125,7 +150,14 @@ static PRBool IsNamedAnchorNode(nsIDOMNode *aNode) return PR_FALSE; } + nsHTMLEditor::nsHTMLEditor() +: nsEditor() +, mTypeInState(nsnull) +, mRules(nsnull) +, mIsComposing(PR_FALSE) +, mMaxTextLength(-1) +, mWrapColumn(0) { // Done in nsEditor // NS_INIT_REFCNT(); @@ -134,562 +166,694 @@ nsHTMLEditor::nsHTMLEditor() nsHTMLEditor::~nsHTMLEditor() { //the autopointers will clear themselves up. + //but we need to also remove the listeners or we have a leak + nsCOMPtrselection; + nsresult result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + nsCOMPtrlistener; + listener = do_QueryInterface(mTypeInState); + if (listener) { + selection->RemoveSelectionListener(listener); + } + } + nsCOMPtr doc; + nsEditor::GetDocument(getter_AddRefs(doc)); + if (doc) + { + nsCOMPtr erP = do_QueryInterface(doc, &result); + if (NS_SUCCEEDED(result) && erP) + { + if (mKeyListenerP) { + erP->RemoveEventListenerByIID(mKeyListenerP, nsIDOMKeyListener::GetIID()); + } + if (mMouseListenerP) { + erP->RemoveEventListenerByIID(mMouseListenerP, nsIDOMMouseListener::GetIID()); + } + if (mTextListenerP) { + erP->RemoveEventListenerByIID(mTextListenerP, nsIDOMTextListener::GetIID()); + } + if (mCompositionListenerP) { + erP->RemoveEventListenerByIID(mCompositionListenerP, nsIDOMCompositionListener::GetIID()); + } + if (mFocusListenerP) { + erP->RemoveEventListenerByIID(mFocusListenerP, nsIDOMFocusListener::GetIID()); + } + if (mDragListenerP) { + erP->RemoveEventListenerByIID(mDragListenerP, nsIDOMDragListener::GetIID()); + } + } + else + NS_NOTREACHED("~nsTextEditor"); + } + + // deleting a null pointer is safe + delete mRules; + + NS_IF_RELEASE(mTypeInState); } -// Adds appropriate AddRef, Release, and QueryInterface methods for derived class -//NS_IMPL_ISUPPORTS_INHERITED(nsHTMLEditor, nsTextEditor, nsIHTMLEditor) +NS_IMPL_ADDREF_INHERITED(nsHTMLEditor, nsEditor) +NS_IMPL_RELEASE_INHERITED(nsHTMLEditor, nsEditor) -//NS_IMPL_ADDREF_INHERITED(Class, Super) -NS_IMETHODIMP_(nsrefcnt) nsHTMLEditor::AddRef(void) -{ - return nsTextEditor::AddRef(); -} -//NS_IMPL_RELEASE_INHERITED(Class, Super) -NS_IMETHODIMP_(nsrefcnt) nsHTMLEditor::Release(void) -{ - return nsTextEditor::Release(); -} - -//NS_IMPL_QUERY_INTERFACE_INHERITED(Class, Super, AdditionalInterface) NS_IMETHODIMP nsHTMLEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) { - if (!aInstancePtr) return NS_ERROR_NULL_POINTER; + if (!aInstancePtr) + return NS_ERROR_NULL_POINTER; + *aInstancePtr = nsnull; + if (aIID.Equals(nsIHTMLEditor::GetIID())) { *aInstancePtr = NS_STATIC_CAST(nsIHTMLEditor*, this); NS_ADDREF_THIS(); return NS_OK; } - return nsTextEditor::QueryInterface(aIID, aInstancePtr); + if (aIID.Equals(nsIEditorMailSupport::GetIID())) { + *aInstancePtr = NS_STATIC_CAST(nsIEditorMailSupport*, this); + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(nsITableEditor::GetIID())) { + *aInstancePtr = NS_STATIC_CAST(nsITableEditor*, this); + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(nsIEditorStyleSheets::GetIID())) { + *aInstancePtr = NS_STATIC_CAST(nsIEditorStyleSheets*, this); + NS_ADDREF_THIS(); + return NS_OK; + } + + return nsEditor::QueryInterface(aIID, aInstancePtr); } NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, - nsIPresShell *aPresShell) + nsIPresShell *aPresShell, PRUint32 aFlags) { - NS_PRECONDITION(nsnull!=aDoc && nsnull!=aPresShell, "bad arg"); - nsresult res=NS_ERROR_NULL_POINTER; - if ((nsnull!=aDoc) && (nsnull!=aPresShell)) + NS_PRECONDITION(aDoc && aPresShell, "bad arg"); + if (!aDoc || !aPresShell) + return NS_ERROR_NULL_POINTER; + + nsresult result = NS_ERROR_NULL_POINTER; + // Init the base editor + result = nsEditor::Init(aDoc, aPresShell, aFlags); + if (NS_OK != result) { return result; } + + // init the type-in state + mTypeInState = new TypeInState(); + if (!mTypeInState) {return NS_ERROR_NULL_POINTER;} + NS_ADDREF(mTypeInState); + + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_OK != result) { return result; } + if (selection) { - res = nsTextEditor::Init(aDoc, aPresShell); - if (NS_SUCCEEDED(res)) - { - // Set up a DTD XXX XXX - // HACK: This should have happened in a document specific way - // in nsEditor::Init(), but we dont' have a way to do that yet - res = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull, - nsIDTD::GetIID(), getter_AddRefs(mDTD)); - if (!mDTD) res = NS_ERROR_FAILURE; + nsCOMPtrlistener; + listener = do_QueryInterface(mTypeInState); + if (listener) { + selection->AddSelectionListener(listener); } } - return res; + + // Init the rules system + InitRules(); + + // get a key listener + result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this); + if (NS_OK != result) { + HandleEventListenerError(); + return result; + } + + // get a mouse listener + result = NS_NewEditorMouseListener(getter_AddRefs(mMouseListenerP), this); + if (NS_OK != result) { + HandleEventListenerError(); + return result; + } + + // get a text listener + result = NS_NewEditorTextListener(getter_AddRefs(mTextListenerP),this); + if (NS_OK !=result) + { +#ifdef DEBUG_TAGUE +printf("nsTextEditor.cpp: failed to get TextEvent Listener\n"); +#endif + HandleEventListenerError(); + return result; + } + + // get a composition listener + result = NS_NewEditorCompositionListener(getter_AddRefs(mCompositionListenerP),this); + if (NS_OK!=result) + { +#ifdef DEBUG_TAGUE +printf("nsTextEditor.cpp: failed to get TextEvent Listener\n"); +#endif + HandleEventListenerError(); + return result; + } + + // get a drag listener + result = NS_NewEditorDragListener(getter_AddRefs(mDragListenerP), this); + if (NS_OK != result) + { + HandleEventListenerError(); + //return result; // XXX: why is this return commented out? + } + + // get a focus listener + result = NS_NewEditorFocusListener(getter_AddRefs(mFocusListenerP), this); + if (NS_OK != result) + { + HandleEventListenerError(); + return result; + } + + // get the DOM event receiver + nsCOMPtr erP; + result = aDoc->QueryInterface(nsIDOMEventReceiver::GetIID(), getter_AddRefs(erP)); + if (!NS_SUCCEEDED(result)) + { + HandleEventListenerError(); + return result; + } + + // register the event listeners with the DOM event reveiver + result = erP->AddEventListenerByIID(mKeyListenerP, nsIDOMKeyListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register key listener"); + result = erP->AddEventListenerByIID(mMouseListenerP, nsIDOMMouseListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register mouse listener"); + result = erP->AddEventListenerByIID(mFocusListenerP, nsIDOMFocusListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register focus listener"); + result = erP->AddEventListenerByIID(mTextListenerP, nsIDOMTextListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register text listener"); + result = erP->AddEventListenerByIID(mCompositionListenerP, nsIDOMCompositionListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register composition listener"); + result = erP->AddEventListenerByIID(mDragListenerP, nsIDOMDragListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register drag listener"); + + result = NS_OK; + EnableUndo(PR_TRUE); + + // Set up a DTD XXX XXX + // HACK: This should have happened in a document specific way + // in nsEditor::Init(), but we dont' have a way to do that yet + result = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull, + nsIDTD::GetIID(), getter_AddRefs(mDTD)); + if (!mDTD) result = NS_ERROR_FAILURE; + + return result; } -void nsHTMLEditor::InitRules() +NS_IMETHODIMP +nsHTMLEditor::GetFlags(PRUint32 *aFlags) +{ + if (!mRules || !aFlags) { return NS_ERROR_NULL_POINTER; } + return mRules->GetFlags(aFlags); +} + + +NS_IMETHODIMP +nsHTMLEditor::SetFlags(PRUint32 aFlags) +{ + if (!mRules) { return NS_ERROR_NULL_POINTER; } + return mRules->SetFlags(aFlags); +} + + +void nsHTMLEditor::InitRules() { // instantiate the rules for this text editor // XXX: we should be told which set of rules to instantiate - mRules = new nsHTMLEditRules(); + if (mFlags & eEditorPlaintextMask) + mRules = new nsTextEditRules(mFlags); + else + mRules = new nsHTMLEditRules(mFlags); + mRules->Init(this); } -NS_IMETHODIMP nsHTMLEditor::SetTextProperty(nsIAtom *aProperty, +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIHTMLEditor methods --- +#pragma mark - +#endif + + +NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue) { - return nsTextEditor::SetTextProperty(aProperty, aAttribute, aValue); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SetTextProperty(aProperty, aAttribute, aValue); +#endif // ENABLE_JS_EDITOR_LOG + + if (!aProperty) { return NS_ERROR_NULL_POINTER; } + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("---------- start nsTextEditor::SetTextProperty %s ----------\n", propCString); } + delete [] propCString; + } + + nsresult result=NS_ERROR_NOT_INITIALIZED; + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + PRBool cancel; + nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty); + result = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) + { + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (PR_TRUE==isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, aValue); + } + else + { + // set the text property for all selected ranges + nsEditor::BeginTransaction(); + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + nsCOMPtrcommonParent; + result = range->GetCommonParent(getter_AddRefs(commonParent)); + if ((NS_SUCCEEDED(result)) && commonParent) + { + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr startParent; nsCOMPtr endParent; + range->GetStartParent(getter_AddRefs(startParent)); + range->GetEndParent(getter_AddRefs(endParent)); + PRBool startIsText = IsTextNode(startParent); + PRBool endIsText = IsTextNode(endParent); + if ((PR_TRUE==startIsText) && (PR_TRUE==endIsText) && + (startParent.get()==endParent.get())) + { // the range is entirely contained within a single text node + // commonParent==aStartParent, so get the "real" parent of the selection + startParent->GetParentNode(getter_AddRefs(commonParent)); + result = SetTextPropertiesForNode(startParent, commonParent, + startOffset, endOffset, + aProperty, aAttribute, aValue); + } + else + { + nsCOMPtr startGrandParent; + startParent->GetParentNode(getter_AddRefs(startGrandParent)); + nsCOMPtr endGrandParent; + endParent->GetParentNode(getter_AddRefs(endGrandParent)); + if (NS_SUCCEEDED(result)) + { + PRBool canCollapseStyleNode = PR_FALSE; + if ((PR_TRUE==startIsText) && (PR_TRUE==endIsText) && + endGrandParent.get()==startGrandParent.get()) + { + result = IntermediateNodesAreInline(range, startParent, startOffset, + endParent, endOffset, + canCollapseStyleNode); + } + if (NS_SUCCEEDED(result)) + { + if (PR_TRUE==canCollapseStyleNode) + { // the range is between 2 nodes that have a common (immediate) grandparent, + // and any intermediate nodes are just inline style nodes + result = SetTextPropertiesForNodesWithSameParent(startParent,startOffset, + endParent, endOffset, + commonParent, + aProperty, aAttribute, aValue); + } + else + { // the range is between 2 nodes that have no simple relationship + result = SetTextPropertiesForNodeWithDifferentParents(range, + startParent,startOffset, + endParent, endOffset, + commonParent, + aProperty, aAttribute, aValue); + } + } + } + } + if (NS_SUCCEEDED(result)) + { // compute a range for the selection + // don't want to actually do anything with selection, because + // we are still iterating through it. Just want to create and remember + // an nsIDOMRange, and later add the range to the selection after clearing it. + // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism + // for setting a compound selection yet. + } + } + } + } + nsEditor::EndTransaction(); + } + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + } + if (gNoisy) {DebugDumpContent(); } // DEBUG + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("---------- end nsTextEditor::SetTextProperty %s ----------\n", propCString); } + delete [] propCString; + } + return result; } -NS_IMETHODIMP nsHTMLEditor::GetTextProperty(nsIAtom *aProperty, +NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue, PRBool &aFirst, PRBool &aAny, PRBool &aAll) { - return nsTextEditor::GetTextProperty(aProperty, aAttribute, aValue, aFirst, aAny, aAll); -} - -NS_IMETHODIMP nsHTMLEditor::RemoveTextProperty(nsIAtom *aProperty, const nsString *aAttribute) -{ - return nsTextEditor::RemoveTextProperty(aProperty, aAttribute); -} - -NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction) -{ - return nsTextEditor::DeleteSelection(aAction); -} - -NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) -{ - return nsTextEditor::InsertText(aStringToInsert); -} - -NS_IMETHODIMP nsHTMLEditor::SetBackgroundColor(const nsString& aColor) -{ -// nsresult result; - NS_ASSERTION(mDoc, "Missing Editor DOM Document"); - - // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - // For initial testing, just set the background on the BODY tag (the document's background) - -// Do this only if setting a table or cell background -// It will be called in nsTextEditor::SetBackgroundColor for the page background -#if 0 //def ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->SetBackgroundColor(aColor); -#endif // ENABLE_JS_EDITOR_LOG - - NS_ASSERTION(mDoc, "Missing Editor DOM Document"); - - // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - // For initial testing, just set the background on the BODY tag (the document's background) - - return nsTextEditor::SetBackgroundColor(aColor); -} - -NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsString& aAttribute, const nsString& aValue) -{ - -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->SetBodyAttribute(aAttribute, aValue); -#endif // ENABLE_JS_EDITOR_LOG - - nsresult res; - // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - - NS_ASSERTION(mDoc, "Missing Editor DOM Document"); - - // Set the background color attribute on the body tag - nsCOMPtr bodyElement; - - res = nsEditor::GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_SUCCEEDED(res) && bodyElement) - { - // Use the editor's method which goes through the transaction system - SetAttribute(bodyElement, aAttribute, aValue); + if (!aProperty) + return NS_ERROR_NULL_POINTER; +/* + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("nsTextEditor::GetTextProperty %s\n", propCString); } + delete [] propCString; } - return res; +*/ + nsresult result=NS_ERROR_NOT_INITIALIZED; + aAny=PR_FALSE; + aAll=PR_TRUE; + aFirst=PR_FALSE; + PRBool first=PR_TRUE; + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + // XXX: should be a while loop, to get each separate range + if ((NS_SUCCEEDED(result)) && currentItem) + { + PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag + nsCOMPtr range( do_QueryInterface(currentItem) ); + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), + getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + iter->Init(range); + nsCOMPtr content; + result = iter->CurrentNode(getter_AddRefs(content)); + while (NS_COMFALSE == iter->IsDone()) + { + //if (gNoisy) { printf(" checking node %p\n", content.get()); } + nsCOMPtrtext; + text = do_QueryInterface(content); + PRBool skipNode = PR_FALSE; + if (text) + { + if (PR_FALSE==isCollapsed && PR_TRUE==first && PR_TRUE==firstNodeInRange) + { + firstNodeInRange = PR_FALSE; + PRInt32 startOffset; + range->GetStartOffset(&startOffset); + PRUint32 count; + text->GetLength(&count); + if (startOffset==(PRInt32)count) + { + //if (gNoisy) { printf(" skipping node %p\n", content.get()); } + skipNode = PR_TRUE; + } + } + } + else + { // handle non-text leaf nodes here + PRBool canContainChildren; + content->CanContainChildren(canContainChildren); + if (PR_TRUE==canContainChildren) + { + //if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); } + skipNode = PR_TRUE; + } + else { + //if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); } + } + } + if (PR_FALSE==skipNode) + { + nsCOMPtrnode; + node = do_QueryInterface(content); + if (node) + { + PRBool isSet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode)); + if (PR_TRUE==first) + { + aFirst = isSet; + first = PR_FALSE; + } + if (PR_TRUE==isSet) { + aAny = PR_TRUE; + } + else { + aAll = PR_FALSE; + } + } + } + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + } + } + } + } + if (PR_FALSE==aAny) { // make sure that if none of the selection is set, we don't report all is set + aAll = PR_FALSE; + } + //if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); } + return result; } -NS_IMETHODIMP nsHTMLEditor::InsertBreak() +NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute) +{ + +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->RemoveTextProperty(aProperty, aAttribute); +#endif // ENABLE_JS_EDITOR_LOG + + if (!aProperty) { return NS_ERROR_NULL_POINTER; } + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("---------- start nsTextEditor::RemoveTextProperty %s ----------\n", propCString); } + delete [] propCString; + } + + nsresult result=NS_ERROR_NOT_INITIALIZED; + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + PRBool cancel; + nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty); + result = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) + { + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (PR_TRUE==isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, nsnull); + } + else + { + // removing text properties can really shuffle text nodes around + // so we need to keep some extra state to restore a reasonable selection + // after we're done + nsCOMPtr parentForSelection; // selection's block parent + PRInt32 rangeStartOffset, rangeEndOffset; + GetTextSelectionOffsetsForRange(selection, getter_AddRefs(parentForSelection), + rangeStartOffset, rangeEndOffset); + nsEditor::BeginTransaction(); + nsCOMPtr startParent, endParent; + PRInt32 startOffset, endOffset; + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtrcurrentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + nsCOMPtrcommonParent; + result = range->GetCommonParent(getter_AddRefs(commonParent)); + if ((NS_SUCCEEDED(result)) && commonParent) + { + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + range->GetStartParent(getter_AddRefs(startParent)); + range->GetEndParent(getter_AddRefs(endParent)); + if (startParent.get()==endParent.get()) + { // the range is entirely contained within a single text node + // commonParent==aStartParent, so get the "real" parent of the selection + startParent->GetParentNode(getter_AddRefs(commonParent)); + result = RemoveTextPropertiesForNode(startParent, commonParent, + startOffset, endOffset, + aProperty, nsnull); + } + else + { + result = RemoveTextPropertiesForNodeWithDifferentParents(startParent,startOffset, + endParent, endOffset, + commonParent, + aProperty, nsnull); + } + if (NS_SUCCEEDED(result)) + { // compute a range for the selection + // don't want to actually do anything with selection, because + // we are still iterating through it. Just want to create and remember + // an nsIDOMRange, and later add the range to the selection after clearing it. + // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism + // for setting a compound selection yet. + } + } + } + } + nsEditor::EndTransaction(); + if (NS_SUCCEEDED(result)) + { + ResetTextSelectionForRange(parentForSelection, rangeStartOffset, rangeEndOffset, selection); + } + } + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + } + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("---------- end nsTextEditor::RemoveTextProperty %s ----------\n", propCString); } + delete [] propCString; + } + return result; +} + +NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ESelectionCollapseDirection aAction) { #ifdef ENABLE_JS_EDITOR_LOG nsAutoJSEditorLogLock logLock(mJSEditorLog); if (mJSEditorLog) - mJSEditorLog->InsertBreak(); + mJSEditorLog->DeleteSelection(aAction); #endif // ENABLE_JS_EDITOR_LOG - nsresult res; if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } nsCOMPtr selection; PRBool cancel= PR_FALSE; - nsAutoEditBatch beginBatching(this); + nsresult result = nsEditor::BeginTransaction(); + if (NS_FAILED(result)) { return result; } // pre-process nsEditor::GetSelection(getter_AddRefs(selection)); - nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertBreak); - res = mRules->WillDoAction(selection, &ruleInfo, &cancel); - if ((PR_FALSE==cancel) && (NS_SUCCEEDED(res))) + nsTextRulesInfo ruleInfo(nsTextEditRules::kDeleteSelection); + ruleInfo.collapsedAction = aAction; + result = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) { - // create the new BR node - nsCOMPtr newNode; - nsAutoString tag("BR"); - res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (NS_SUCCEEDED(res) && newNode) - { - // set the selection to the new node - nsCOMPtrparent; - res = newNode->GetParentNode(getter_AddRefs(parent)); - if (NS_SUCCEEDED(res) && parent) - { - PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not - nsCOMPtrnextNode; - newNode->GetNextSibling(getter_AddRefs(nextNode)); - if (nextNode) - { - nsCOMPtrnextTextNode; - nextTextNode = do_QueryInterface(nextNode); - if (!nextTextNode) { - nextNode = do_QueryInterface(newNode); - } - else { - offsetInParent=0; - } - } - else { - nextNode = do_QueryInterface(newNode); - } - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(res)) - { - if (-1==offsetInParent) - { - nextNode->GetParentNode(getter_AddRefs(parent)); - res = GetChildOffset(nextNode, parent, offsetInParent); - if (NS_SUCCEEDED(res)) { - selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break - } - } - else - { - selection->Collapse(nextNode, offsetInParent); - } - } - } - } - // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE - res = mRules->DidDoAction(selection, &ruleInfo, res); + result = DeleteSelectionImpl(aAction); + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); } - return res; + nsresult endTxnResult = nsEditor::EndTransaction(); // don't return this result! + NS_ASSERTION ((NS_SUCCEEDED(endTxnResult)), "bad end transaction result"); + + return result; } -NS_IMETHODIMP nsHTMLEditor::GetParagraphFormat(nsString& aParagraphFormat) -{ - nsresult res = NS_ERROR_NOT_INITIALIZED; - - return res; -} - -NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat) +NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) { #ifdef ENABLE_JS_EDITOR_LOG nsAutoJSEditorLogLock logLock(mJSEditorLog); if (mJSEditorLog) - mJSEditorLog->SetParagraphFormat(aParagraphFormat); + mJSEditorLog->InsertText(aStringToInsert); #endif // ENABLE_JS_EDITOR_LOG - nsresult res = NS_ERROR_NOT_INITIALIZED; - //Kinda sad to waste memory just to force lower case - nsAutoString tag = aParagraphFormat; - tag.ToLowerCase(); - if (tag == "normal" || tag == "p") { - res = RemoveParagraphStyle(); - } else if (tag == "li") { - res = InsertList("ul"); - } else { - res = ReplaceBlockParent(tag); - } - return res; -} + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - -// Methods shared with the base editor. -// Note: We could call each of these via nsTextEditor -- is that better? -NS_IMETHODIMP nsHTMLEditor::EnableUndo(PRBool aEnable) -{ - return nsTextEditor::EnableUndo(aEnable); -} - -NS_IMETHODIMP nsHTMLEditor::Undo(PRUint32 aCount) -{ - return nsTextEditor::Undo(aCount); -} - -NS_IMETHODIMP nsHTMLEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo) -{ - return nsTextEditor::CanUndo(aIsEnabled, aCanUndo); -} - -NS_IMETHODIMP nsHTMLEditor::Redo(PRUint32 aCount) -{ - return nsTextEditor::Redo(aCount); -} - -NS_IMETHODIMP nsHTMLEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo) -{ - return nsTextEditor::CanRedo(aIsEnabled, aCanRedo); -} - -NS_IMETHODIMP nsHTMLEditor::BeginTransaction() -{ - return nsTextEditor::BeginTransaction(); -} - -NS_IMETHODIMP nsHTMLEditor::EndTransaction() -{ - return nsTextEditor::EndTransaction(); -} - -NS_IMETHODIMP nsHTMLEditor::MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::MoveSelectionUp(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::MoveSelectionDown(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::MoveSelectionNext(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::MoveSelectionPrevious(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::SelectNext(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::SelectPrevious(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::SelectAll() -{ - return nsTextEditor::SelectAll(); -} - -NS_IMETHODIMP nsHTMLEditor::BeginningOfDocument() -{ - return nsEditor::BeginningOfDocument(); -} - -NS_IMETHODIMP nsHTMLEditor::EndOfDocument() -{ - return nsEditor::EndOfDocument(); -} - -NS_IMETHODIMP nsHTMLEditor::ScrollUp(nsIAtom *aIncrement) -{ - return nsTextEditor::ScrollUp(aIncrement); -} - -NS_IMETHODIMP nsHTMLEditor::ScrollDown(nsIAtom *aIncrement) -{ - return nsTextEditor::ScrollDown(aIncrement); -} - -NS_IMETHODIMP nsHTMLEditor::ScrollIntoView(PRBool aScrollToBegin) -{ - return nsTextEditor::ScrollIntoView(aScrollToBegin); -} - -NS_IMETHODIMP nsHTMLEditor::Save() -{ - return nsTextEditor::Save(); -} - -NS_IMETHODIMP nsHTMLEditor::SaveAs(PRBool aSavingCopy) -{ - return nsTextEditor::SaveAs(aSavingCopy); -} - -NS_IMETHODIMP nsHTMLEditor::Cut() -{ - return nsTextEditor::Cut(); -} - -NS_IMETHODIMP nsHTMLEditor::Copy() -{ - return nsTextEditor::Copy(); -} - -NS_IMETHODIMP nsHTMLEditor::Paste() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Paste(); -#endif // ENABLE_JS_EDITOR_LOG - - nsString stuffToPaste; - - // Get Clipboard Service - nsIClipboard* clipboard; - nsresult rv = nsServiceManager::GetService(kCClipboardCID, - nsIClipboard::GetIID(), - (nsISupports **)&clipboard); - - // Create generic Transferable for getting the data - nsCOMPtr trans; - rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, - nsITransferable::GetIID(), - (void**) getter_AddRefs(trans)); - if (NS_SUCCEEDED(rv)) - { - // Get the nsITransferable interface for getting the data from the clipboard - if (trans) - { - // Create the desired DataFlavor for the type of data - // we want to get out of the transferable - nsAutoString htmlFlavor(kHTMLMime); - nsAutoString textFlavor(kTextMime); - nsAutoString imageFlavor(kJPEGImageMime); - - trans->AddDataFlavor(&htmlFlavor); - trans->AddDataFlavor(&textFlavor); - trans->AddDataFlavor(&imageFlavor); - - // Get the Data from the clipboard - if (NS_SUCCEEDED(clipboard->GetData(trans))) - { - nsAutoString flavor; - char * data; - PRUint32 len; - if (NS_SUCCEEDED(trans->GetAnyTransferData(&flavor, (void **)&data, &len))) - { -#ifdef DEBUG_akkana - printf("Got flavor [%s]\n", flavor.ToNewCString()); -#endif - if (flavor.Equals(htmlFlavor)) - { - if (data && len > 0) // stuffToPaste is ready for insertion into the content - { - stuffToPaste.SetString(data, len); - rv = InsertHTML(stuffToPaste); - } - } - else if (flavor.Equals(textFlavor)) - { - if (data && len > 0) // stuffToPaste is ready for insertion into the content - { - stuffToPaste.SetString(data, len); - rv = InsertText(stuffToPaste); - } - } - else if (flavor.Equals(imageFlavor)) - { - // Insert Image code here - printf("Don't know how to insert an image yet!\n"); - //nsIImage* image = (nsIImage *)data; - //NS_RELEASE(image); - rv = NS_ERROR_FAILURE; // for now give error code - } - } - - } - } - } - nsServiceManager::ReleaseService(kCClipboardCID, clipboard); - - return rv; -} - -// -// HTML PasteAsQuotation: Paste in a blockquote type=cite -// -NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->PasteAsQuotation(); -#endif // ENABLE_JS_EDITOR_LOG - - nsAutoString citation(""); - return PasteAsCitedQuotation(citation); -} - -NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->PasteAsCitedQuotation(aCitation); -#endif // ENABLE_JS_EDITOR_LOG - - nsAutoEditBatch beginBatching(this); - nsCOMPtr newNode; - nsAutoString tag("blockquote"); - nsresult res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (NS_FAILED(res) || !newNode) - return res; - - // Try to set type=cite. Ignore it if this fails. - nsCOMPtr newElement (do_QueryInterface(newNode)); - if (newElement) - { - nsAutoString type ("type"); - nsAutoString cite ("cite"); - newElement->SetAttribute(type, cite); - } - - // Set the selection to the underneath the node we just inserted: nsCOMPtr selection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res) || !selection) - { -#ifdef DEBUG_akkana - printf("Can't get selection!"); -#endif - } + PRBool cancel= PR_FALSE; - res = selection->Collapse(newNode, 0); - if (NS_FAILED(res)) - { -#ifdef DEBUG_akkana - printf("Couldn't collapse"); -#endif - } + // pre-process + nsEditor::GetSelection(getter_AddRefs(selection)); + nsString resultString; + PlaceholderTxn *placeholderTxn=nsnull; + nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertText); + ruleInfo.placeTxn = &placeholderTxn; + ruleInfo.inString = &aStringToInsert; + ruleInfo.outString = &resultString; + ruleInfo.typeInState = *mTypeInState; + ruleInfo.maxLength = mMaxTextLength; - res = Paste(); - return res; + nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) + { + result = InsertTextImpl(resultString); + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + if (placeholderTxn) + placeholderTxn->SetAbsorb(PR_FALSE); // this ends the merging of txns into placeholderTxn + + return result; } -NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertAsQuotation(aQuotedText); -#endif // ENABLE_JS_EDITOR_LOG - - nsAutoString citation (""); - return InsertAsCitedQuotation(aQuotedText, citation); -} - -NS_IMETHODIMP nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText, - const nsString& aCitation) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertAsCitedQuotation(aQuotedText, aCitation); -#endif // ENABLE_JS_EDITOR_LOG - - nsAutoEditBatch beginBatching(this); - nsCOMPtr newNode; - nsAutoString tag("blockquote"); - nsresult res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (NS_FAILED(res) || !newNode) - return res; - - // Try to set type=cite. Ignore it if this fails. - nsCOMPtr newElement (do_QueryInterface(newNode)); - if (newElement) - { - nsAutoString type ("type"); - nsAutoString cite ("cite"); - newElement->SetAttribute(type, cite); - - if (aCitation.Length() > 0) - newElement->SetAttribute(cite, aCitation); - } - - res = InsertHTML(aQuotedText); - return res; -} NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) { @@ -798,39 +962,367 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) } -NS_IMETHODIMP nsHTMLEditor::OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags) +NS_IMETHODIMP nsHTMLEditor::InsertBreak() { - return nsTextEditor::OutputToString(aOutputString, aFormatType, aFlags); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->InsertBreak(); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res; + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + PRBool cancel= PR_FALSE; + + nsAutoEditBatch beginBatching(this); + + // pre-process + nsEditor::GetSelection(getter_AddRefs(selection)); + nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertBreak); + res = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(res))) + { + // create the new BR node + nsCOMPtr newNode; + nsAutoString tag("BR"); + res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (NS_SUCCEEDED(res) && newNode) + { + // set the selection to the new node + nsCOMPtrparent; + res = newNode->GetParentNode(getter_AddRefs(parent)); + if (NS_SUCCEEDED(res) && parent) + { + PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not + nsCOMPtrnextNode; + newNode->GetNextSibling(getter_AddRefs(nextNode)); + if (nextNode) + { + nsCOMPtrnextTextNode; + nextTextNode = do_QueryInterface(nextNode); + if (!nextTextNode) { + nextNode = do_QueryInterface(newNode); + } + else { + offsetInParent=0; + } + } + else { + nextNode = do_QueryInterface(newNode); + } + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(res)) + { + if (-1==offsetInParent) + { + nextNode->GetParentNode(getter_AddRefs(parent)); + res = GetChildOffset(nextNode, parent, offsetInParent); + if (NS_SUCCEEDED(res)) { + selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break + } + } + else + { + selection->Collapse(nextNode, offsetInParent); + } + } + } + } + // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE + res = mRules->DidDoAction(selection, &ruleInfo, res); + } + + return res; } -NS_IMETHODIMP nsHTMLEditor::OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharset, - PRUint32 aFlags) + +NS_IMETHODIMP +nsHTMLEditor::InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection) { - return nsTextEditor::OutputToStream(aOutputStream, aFormatType, - aCharset, aFlags); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->InsertElement(aElement, aDeleteSelection); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res = NS_ERROR_NOT_INITIALIZED; + + if (!aElement) + return NS_ERROR_NULL_POINTER; + + nsAutoEditBatch beginBatching(this); + // For most elements, set caret after inserting + //PRBool setCaretAfterElement = PR_TRUE; + + nsCOMPtrselection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (!NS_SUCCEEDED(res) || !selection) + return NS_ERROR_FAILURE; + + // Clear current selection. + // Should put caret at anchor point? + if (!aDeleteSelection) + { + PRBool collapseAfter = PR_TRUE; + // Named Anchor is a special case, + // We collapse to insert element BEFORE the selection + // For all other tags, we insert AFTER the selection + nsCOMPtr node = do_QueryInterface(aElement); + if (IsNamedAnchorNode(node)) + collapseAfter = PR_FALSE; + + if (collapseAfter) + { + // Default behavior is to collapse to the end of the selection + selection->ClearSelection(); + } else { + // Collapse to the start of the selection, + // We must explore the first range and find + // its parent and starting offset of selection + // TODO: Move this logic to a new method nsIDOMSelection::CollapseToStart()??? + nsCOMPtr firstRange; + res = selection->GetRangeAt(0, getter_AddRefs(firstRange)); + if (NS_SUCCEEDED(res) && firstRange) + { + nsCOMPtr parent; + res = firstRange->GetCommonParent(getter_AddRefs(parent)); + if (NS_SUCCEEDED(res) && parent) + { + PRInt32 startOffset; + firstRange->GetStartOffset(&startOffset); + selection->Collapse(parent, startOffset); + } else { + // Very unlikely, but collapse to the end if we failed above + selection->ClearSelection(); + } + } + } + } + + nsCOMPtr parentSelectedNode; + PRInt32 offsetForInsert; + res = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); + if (NS_SUCCEEDED(res) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetForInsert)) && parentSelectedNode) + { +#ifdef DEBUG_cmanske + { + nsAutoString name; + parentSelectedNode->GetNodeName(name); + printf("InsertElement: Anchor node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n", offsetForInsert); + } +#endif + nsAutoString tagName; + aElement->GetNodeName(tagName); + tagName.ToLowerCase(); + nsCOMPtr parent = parentSelectedNode; + nsCOMPtr topChild = parentSelectedNode; + nsCOMPtr tmp; + nsAutoString parentTagName; + PRBool isRoot; + + // Search up the parent chain to find a suitable container + while (!CanContainTag(parent, tagName)) + { + // If the current parent is a root (body or table cell) + // then go no further - we can't insert + parent->GetNodeName(parentTagName); + res = IsRootTag(parentTagName, isRoot); + if (!NS_SUCCEEDED(res) || isRoot) + return NS_ERROR_FAILURE; + // Get the next parent + parent->GetParentNode(getter_AddRefs(tmp)); + if (!tmp) + return NS_ERROR_FAILURE; + topChild = parent; + parent = tmp; + } +#ifdef DEBUG_cmanske + { + nsAutoString name; + parent->GetNodeName(name); + printf("Parent node to insert under: "); + wprintf(name.GetUnicode()); + printf("\n"); + topChild->GetNodeName(name); + printf("TopChild to split: "); + wprintf(name.GetUnicode()); + printf("\n"); + } +#endif + if (parent != topChild) + { + // we need to split some levels above the original selection parent + res = SplitNodeDeep(topChild, parentSelectedNode, offsetForInsert); + if (NS_FAILED(res)) + return res; + // topChild went to the right on the split + // so this is the offset to insert at + offsetForInsert = GetIndexOf(parent,topChild); + } + // Now we can insert the new node + res = InsertNode(aElement, parent, offsetForInsert); + + // Set caret after element, but check for special case + // of inserting table-related elements: set in first cell instead + if (!SetCaretInTableCell(aElement)) + res = SetCaretAfterElement(aElement); + + } + return res; +} + + +NS_IMETHODIMP +nsHTMLEditor::DeleteSelectionAndCreateNode(const nsString& aTag, + nsIDOMNode ** aNewNode) +{ + nsCOMPtr parentSelectedNode; + PRInt32 offsetOfNewNode; + nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, + offsetOfNewNode); + if (!NS_SUCCEEDED(result)) + return result; + + nsCOMPtr newNode; + result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode, + getter_AddRefs(newNode)); + + *aNewNode = newNode; + + // we want the selection to be just after the new node + nsCOMPtr selection; + result = GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + selection->Collapse(parentSelectedNode, offsetOfNewNode+1); + + return result; } NS_IMETHODIMP -nsHTMLEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) +nsHTMLEditor::SelectElement(nsIDOMElement* aElement) { - return nsTextEditor::CopyAttributes(aDestNode, aSourceNode); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SelectElement(aElement); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res = NS_ERROR_NULL_POINTER; + + // Must be sure that element is contained in the document body + if (IsElementInBody(aElement)) + { + nsCOMPtr selection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(res) && selection) + { + nsCOMPtrparent; + res = aElement->GetParentNode(getter_AddRefs(parent)); + if (NS_SUCCEEDED(res) && parent) + { + PRInt32 offsetInParent; + res = GetChildOffset(aElement, parent, offsetInParent); + + if (NS_SUCCEEDED(res)) + { + // Collapse selection to just before desired element, + selection->Collapse(parent, offsetInParent); + // then extend it to just after + selection->Extend(parent, offsetInParent+1); + } + } + } + } + return res; } NS_IMETHODIMP -nsHTMLEditor::ApplyStyleSheet(const nsString& aURL) +nsHTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement) { - return nsTextEditor::ApplyStyleSheet(aURL); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SetCaretAfterElement(aElement); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res = NS_ERROR_NULL_POINTER; + + // Be sure the element is contained in the document body + if (aElement && IsElementInBody(aElement)) + { + nsCOMPtr selection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(res) && selection) + { + nsCOMPtrparent; + res = aElement->GetParentNode(getter_AddRefs(parent)); + if (NS_SUCCEEDED(res) && parent) + { + PRInt32 offsetInParent; + res = GetChildOffset(aElement, parent, offsetInParent); + if (NS_SUCCEEDED(res)) + { + // Collapse selection to just after desired element, + selection->Collapse(parent, offsetInParent+1); +#ifdef DEBUG_cmanske + { + nsAutoString name; + parent->GetNodeName(name); + printf("SetCaretAfterElement: Parent node: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n\nHTML:\n", offsetInParent+1); + nsString Format("text/html"); + nsString ContentsAs; + OutputToString(ContentsAs, Format, 2); + wprintf(ContentsAs.GetUnicode()); + } +#endif + } + } + } + } + return res; +} + + +NS_IMETHODIMP nsHTMLEditor::GetParagraphFormat(nsString& aParagraphFormat) +{ + nsresult res = NS_ERROR_NOT_INITIALIZED; + + return res; +} + +NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SetParagraphFormat(aParagraphFormat); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res = NS_ERROR_NOT_INITIALIZED; + //Kinda sad to waste memory just to force lower case + nsAutoString tag = aParagraphFormat; + tag.ToLowerCase(); + if (tag == "normal" || tag == "p") { + res = RemoveParagraphStyle(); + } else if (tag == "li") { + res = InsertList("ul"); + } else { + res = ReplaceBlockParent(tag); + } + return res; } -//================================================================ -// HTML Editor methods -// -// Note: Table Editing methods are implemented in EditTable.cpp -// // get the paragraph style(s) for the selection NS_IMETHODIMP @@ -996,354 +1488,6 @@ nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) return res; } -NS_IMETHODIMP -nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, - nsString &aParentTag, - BlockTransformationType aTransformation) -{ - if (!aNode) { return NS_ERROR_NULL_POINTER; } - if (gNoisy) - { - char *tag = aParentTag.ToNewCString(); - printf("---------- ReParentContentOfNode(%p,%s,%d) -----------\n", aNode, tag, aTransformation); - delete [] tag; - } - // find the current block parent, or just use aNode if it is a block node - nsCOMPtrblockParentElement; - nsCOMPtrnodeToReParent; // this is the node we'll operate on, by default it's aNode - nsresult res = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(nodeToReParent)); - PRBool nodeIsInline; - PRBool nodeIsBlock=PR_FALSE; - nsTextEditor::IsNodeInline(aNode, nodeIsInline); - if (PR_FALSE==nodeIsInline) - { - nsresult QIResult; - nsCOMPtrnodeAsText; - QIResult = aNode->QueryInterface(nsIDOMCharacterData::GetIID(), getter_AddRefs(nodeAsText)); - if (NS_FAILED(QIResult) || !nodeAsText) { - nodeIsBlock=PR_TRUE; - } - } - // if aNode is the block parent, then the node to reparent is one of its children - if (PR_TRUE==nodeIsBlock) - { - res = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(blockParentElement)); - if (NS_SUCCEEDED(res) && blockParentElement) { - res = aNode->GetFirstChild(getter_AddRefs(nodeToReParent)); - } - } - else { // we just need to get the block parent of aNode - res = nsTextEditor::GetBlockParent(aNode, getter_AddRefs(blockParentElement)); - } - // at this point, we must have a good res, a node to reparent, and a block parent - if (!nodeToReParent) { return NS_ERROR_UNEXPECTED;} - if (!blockParentElement) { return NS_ERROR_NULL_POINTER;} - if (NS_SUCCEEDED(res)) - { - nsCOMPtr newParentNode; - nsCOMPtr blockParentNode = do_QueryInterface(blockParentElement); - // we need to treat nodes directly inside the body differently - nsAutoString parentTag; - blockParentElement->GetTagName(parentTag); - PRBool isRoot; - IsRootTag(parentTag, isRoot); - if (PR_TRUE==isRoot) - { - // if nodeToReParent is a text node, we have Text. - // re-parent Text into a new at the offset of Text in - // so we end up with Text - // ignore aTransformation, replaces act like inserts - nsCOMPtr nodeAsText = do_QueryInterface(nodeToReParent); - if (nodeAsText) - { - res = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, - aTransformation, getter_AddRefs(newParentNode)); - } - else - { // this is the case of an insertion point between 2 non-text objects - // XXX: how to you know it's an insertion point??? - PRInt32 offsetInParent=0; - res = GetChildOffset(nodeToReParent, blockParentNode, offsetInParent); - NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); - // otherwise, just create the block parent at the selection - res = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, - getter_AddRefs(newParentNode)); - // XXX: need to move some of the children of blockParentNode into the newParentNode? - // XXX: need to create a bogus text node inside this new block? - // that means, I need to generalize bogus node handling - } - } - else - { // the block parent is not a ROOT, - // for the selected block content, transform blockParentNode - if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) || - (eInsertParent==aTransformation)) - { - if (gNoisy) { DebugDumpContent(); } // DEBUG - res = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, - aTransformation, getter_AddRefs(newParentNode)); - if ((NS_SUCCEEDED(res)) && (newParentNode) && (eReplaceParent==aTransformation)) - { - PRBool hasChildren; - blockParentNode->HasChildNodes(&hasChildren); - if (PR_FALSE==hasChildren) - { - res = nsEditor::DeleteNode(blockParentNode); - if (gNoisy) - { - printf("deleted old block parent node %p\n", blockParentNode.get()); - DebugDumpContent(); // DEBUG - } - } - } - } - else { // otherwise, it's a no-op - if (gNoisy) { printf("AddBlockParent is a no-op for this collapsed selection.\n"); } - } - } - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, - nsString &aParentTag, - nsIDOMNode *aBlockParentNode, - nsString &aBlockParentTag, - BlockTransformationType aTransformation, - nsIDOMNode **aNewParentNode) -{ - if (!aNode || !aBlockParentNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; } - nsCOMPtr blockParentNode = do_QueryInterface(aBlockParentNode); - PRBool removeBlockParent = PR_FALSE; - PRBool removeBreakBefore = PR_FALSE; - PRBool removeBreakAfter = PR_FALSE; - nsCOMPtrancestor; - nsresult res = aNode->GetParentNode(getter_AddRefs(ancestor)); - nsCOMPtrpreviousAncestor = do_QueryInterface(aNode); - while (NS_SUCCEEDED(res) && ancestor) - { - nsCOMPtrancestorElement = do_QueryInterface(ancestor); - nsAutoString ancestorTag; - ancestorElement->GetTagName(ancestorTag); - if (ancestorTag.EqualsIgnoreCase(aBlockParentTag)) - { - break; // previousAncestor will contain the node to operate on - } - previousAncestor = do_QueryInterface(ancestor); - res = ancestorElement->GetParentNode(getter_AddRefs(ancestor)); - } - // now, previousAncestor is the node we are operating on - nsCOMPtrleftNode, rightNode; - res = GetBlockSection(previousAncestor, - getter_AddRefs(leftNode), - getter_AddRefs(rightNode)); - if ((NS_SUCCEEDED(res)) && leftNode && rightNode) - { - // determine some state for managing
s around the new block - PRBool isSubordinateBlock = PR_FALSE; // if true, the content is already in a subordinate block - PRBool isRootBlock = PR_FALSE; // if true, the content is in a root block - nsCOMPtrblockParentElement = do_QueryInterface(blockParentNode); - if (blockParentElement) - { - nsAutoString blockParentTag; - blockParentElement->GetTagName(blockParentTag); - IsSubordinateBlock(blockParentTag, isSubordinateBlock); - IsRootTag(blockParentTag, isRootBlock); - } - - if (PR_TRUE==isRootBlock) - { // we're creating a block element where a block element did not previously exist - removeBreakBefore = PR_TRUE; - removeBreakAfter = PR_TRUE; - } - - // apply the transformation - PRInt32 offsetInParent=0; - if (eInsertParent==aTransformation || PR_TRUE==isRootBlock) - { - res = GetChildOffset(leftNode, blockParentNode, offsetInParent); - NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); - res = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); - if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } - } - else - { - nsCOMPtr grandParent; - res = blockParentNode->GetParentNode(getter_AddRefs(grandParent)); - if ((NS_SUCCEEDED(res)) && grandParent) - { - nsCOMPtrfirstChildNode, lastChildNode; - blockParentNode->GetFirstChild(getter_AddRefs(firstChildNode)); - blockParentNode->GetLastChild(getter_AddRefs(lastChildNode)); - if (firstChildNode==leftNode && lastChildNode==rightNode) - { - res = GetChildOffset(blockParentNode, grandParent, offsetInParent); - NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); - res = nsTextEditor::CreateNode(aParentTag, grandParent, offsetInParent, aNewParentNode); - if (gNoisy) { printf("created a node in grandParent at offset %d\n", offsetInParent); } - } - else - { - // We're in the case where the content of blockParentNode is separated by
's, - // creating multiple block content ranges. - // Split blockParentNode around the blockContent - if (gNoisy) { printf("splitting a node because of
s\n"); } - nsCOMPtr newLeftNode; - if (firstChildNode!=leftNode) - { - res = GetChildOffset(leftNode, blockParentNode, offsetInParent); - if (gNoisy) { printf("splitting left at %d\n", offsetInParent); } - res = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); - // after this split, blockParentNode still contains leftNode and rightNode - } - if (lastChildNode!=rightNode) - { - res = GetChildOffset(rightNode, blockParentNode, offsetInParent); - offsetInParent++; - if (gNoisy) { printf("splitting right at %d\n", offsetInParent); } - res = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); - blockParentNode = do_QueryInterface(newLeftNode); - } - res = GetChildOffset(leftNode, blockParentNode, offsetInParent); - NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); - res = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); - if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } - // what we need to do here is remove the existing block parent when we're all done. - removeBlockParent = PR_TRUE; - } - } - } - if ((NS_SUCCEEDED(res)) && *aNewParentNode) - { // move all the children/contents of blockParentNode to aNewParentNode - nsCOMPtrchildNode = do_QueryInterface(rightNode); - nsCOMPtrpreviousSiblingNode; - while (NS_SUCCEEDED(res) && childNode) - { - childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); - // explicitly delete of childNode from it's current parent - // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! - res = nsEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) - { - res = nsEditor::InsertNode(childNode, *aNewParentNode, 0); - if (gNoisy) - { - printf("re-parented sibling node %p\n", childNode.get()); - } - } - if (childNode==leftNode || rightNode==leftNode) { - break; - } - childNode = do_QueryInterface(previousSiblingNode); - } // end while loop - } - // clean up the surrounding content to maintain vertical whitespace - if (NS_SUCCEEDED(res)) - { - // if the prior node is a
and we did something to change vertical whitespacing, delete the
- nsCOMPtr brNode; - res = GetPriorNode(leftNode, PR_TRUE, getter_AddRefs(brNode)); - if (NS_SUCCEEDED(res) && brNode) - { - nsCOMPtr brContent = do_QueryInterface(brNode); - if (brContent) - { - nsCOMPtr brContentTag; - brContent->GetTag(*getter_AddRefs(brContentTag)); - if (nsIEditProperty::br==brContentTag.get()) { - res = DeleteNode(brNode); - } - } - } - - // if the next node is a
and we did something to change vertical whitespacing, delete the
- if (NS_SUCCEEDED(res)) - { - res = GetNextNode(rightNode, PR_TRUE, getter_AddRefs(brNode)); - if (NS_SUCCEEDED(res) && brNode) - { - nsCOMPtr brContent = do_QueryInterface(brNode); - if (brContent) - { - nsCOMPtr brContentTag; - brContent->GetTag(*getter_AddRefs(brContentTag)); - if (nsIEditProperty::br==brContentTag.get()) { - res = DeleteNode(brNode); - } - } - } - } - } - if ((NS_SUCCEEDED(res)) && (PR_TRUE==removeBlockParent)) - { // we determined we need to remove the previous block parent. Do it! - // go through list backwards so deletes don't interfere with the iteration - nsCOMPtr childNodes; - res = blockParentNode->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(res)) && (childNodes)) - { - nsCOMPtrgrandParent; - blockParentNode->GetParentNode(getter_AddRefs(grandParent)); - //PRInt32 offsetInParent; - res = GetChildOffset(blockParentNode, grandParent, offsetInParent); - PRUint32 childCount; - childNodes->GetLength(&childCount); - PRInt32 i=childCount-1; - for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) - { - nsCOMPtr childNode; - res = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(res)) && (childNode)) - { - res = nsTextEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); - } - } - } - if (gNoisy) { printf("removing the old block parent %p\n", blockParentNode.get()); } - res = nsTextEditor::DeleteNode(blockParentNode); - } - } - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, - nsString &aParentTag, - BlockTransformationType aTranformation) -{ - if (!aRange) { return NS_ERROR_NULL_POINTER; } - nsresult res; - nsISupportsArray *blockSections; - res = NS_NewISupportsArray(&blockSections); - if ((NS_SUCCEEDED(res)) && blockSections) - { - res = GetBlockSectionsForRange(aRange, blockSections); - if (NS_SUCCEEDED(res)) - { - nsIDOMRange *subRange; - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - while (subRange && (NS_SUCCEEDED(res))) - { - nsCOMPtrstartParent; - res = subRange->GetStartParent(getter_AddRefs(startParent)); - if (NS_SUCCEEDED(res) && startParent) - { - if (gNoisy) { printf("ReParentContentOfRange calling ReParentContentOfNode\n"); } - res = ReParentContentOfNode(startParent, aParentTag, aTranformation); - } - NS_RELEASE(subRange); - blockSections->RemoveElementAt(0); - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - } - } - NS_RELEASE(blockSections); - } - return res; -} NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyle() @@ -1383,88 +1527,6 @@ nsHTMLEditor::RemoveParagraphStyle() return res; } -NS_IMETHODIMP -nsHTMLEditor::RemoveParagraphStyleFromRange(nsIDOMRange *aRange) -{ - if (!aRange) { return NS_ERROR_NULL_POINTER; } - nsresult res; - nsISupportsArray *blockSections; - res = NS_NewISupportsArray(&blockSections); - if ((NS_SUCCEEDED(res)) && blockSections) - { - res = GetBlockSectionsForRange(aRange, blockSections); - if (NS_SUCCEEDED(res)) - { - nsIDOMRange *subRange; - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - while (subRange && (NS_SUCCEEDED(res))) - { - res = RemoveParagraphStyleFromBlockContent(subRange); - NS_RELEASE(subRange); - blockSections->RemoveElementAt(0); - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - } - } - NS_RELEASE(blockSections); - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) -{ - if (!aRange) { return NS_ERROR_NULL_POINTER; } - nsresult res; - nsCOMPtrstartParent; - aRange->GetStartParent(getter_AddRefs(startParent)); - nsCOMPtrblockParentElement; - res = nsTextEditor::GetBlockParent(startParent, getter_AddRefs(blockParentElement)); - while ((NS_SUCCEEDED(res)) && blockParentElement) - { - nsAutoString blockParentTag; - blockParentElement->GetTagName(blockParentTag); - PRBool isSubordinateBlock; - IsSubordinateBlock(blockParentTag, isSubordinateBlock); - if (PR_FALSE==isSubordinateBlock) { - break; - } - else - { - // go through list backwards so deletes don't interfere with the iteration - nsCOMPtr childNodes; - res = blockParentElement->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(res)) && (childNodes)) - { - nsCOMPtrgrandParent; - blockParentElement->GetParentNode(getter_AddRefs(grandParent)); - PRInt32 offsetInParent; - res = GetChildOffset(blockParentElement, grandParent, offsetInParent); - PRUint32 childCount; - childNodes->GetLength(&childCount); - PRInt32 i=childCount-1; - for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) - { - nsCOMPtr childNode; - res = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(res)) && (childNode)) - { - res = nsTextEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); - } - } - } - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::DeleteNode(blockParentElement); - } - } - } - res = nsTextEditor::GetBlockParent(startParent, getter_AddRefs(blockParentElement)); - } - return res; -} - - NS_IMETHODIMP nsHTMLEditor::RemoveParent(const nsString &aParentTag) { @@ -1503,94 +1565,98 @@ nsHTMLEditor::RemoveParent(const nsString &aParentTag) return res; } -NS_IMETHODIMP -nsHTMLEditor::RemoveParentFromRange(const nsString &aParentTag, nsIDOMRange *aRange) +NS_IMETHODIMP +nsHTMLEditor::InsertList(const nsString& aListType) { - if (!aRange) { return NS_ERROR_NULL_POINTER; } +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->InsertList(aListType); +#endif // ENABLE_JS_EDITOR_LOG + nsresult res; - nsISupportsArray *blockSections; - res = NS_NewISupportsArray(&blockSections); - if ((NS_SUCCEEDED(res)) && blockSections) + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + PRBool cancel= PR_FALSE; + + nsAutoEditBatch beginBatching(this); + + // pre-process + nsEditor::GetSelection(getter_AddRefs(selection)); + nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeList); + if (aListType == "ol") ruleInfo.bOrdered = PR_TRUE; + else ruleInfo.bOrdered = PR_FALSE; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if (cancel || (NS_FAILED(res))) return res; + + // Find out if the selection is collapsed: + if (NS_FAILED(res) || !selection) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + nsCOMPtr node; + PRInt32 offset; + + res = GetStartNodeAndOffset(selection, &node, &offset); + if (!node) res = NS_ERROR_FAILURE; + if (NS_FAILED(res)) return res; + + if (isCollapsed) { - res = GetBlockSectionsForRange(aRange, blockSections); - if (NS_SUCCEEDED(res)) + // have to find a place to put the list + nsCOMPtr parent = node; + nsCOMPtr topChild = node; + nsCOMPtr tmp; + + while ( !CanContainTag(parent, aListType)) { - nsIDOMRange *subRange; - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - while (subRange && (NS_SUCCEEDED(res))) - { - res = RemoveParentFromBlockContent(aParentTag, subRange); - NS_RELEASE(subRange); - blockSections->RemoveElementAt(0); - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - } + parent->GetParentNode(getter_AddRefs(tmp)); + if (!tmp) return NS_ERROR_FAILURE; + topChild = parent; + parent = tmp; } - NS_RELEASE(blockSections); + + if (parent != node) + { + // we need to split up to the child of parent + res = SplitNodeDeep(topChild, node, offset); + if (NS_FAILED(res)) return res; + // topChild already went to the right on the split + // so we don't need to add one to offset when figuring + // out where to plop list + offset = GetIndexOf(parent,topChild); + } + + // make a list + nsCOMPtr newList; + res = CreateNode(aListType, parent, offset, getter_AddRefs(newList)); + if (NS_FAILED(res)) return res; + // make a list item + nsAutoString tag("li"); + nsCOMPtr newItem; + res = CreateNode(tag, newList, 0, getter_AddRefs(newItem)); + if (NS_FAILED(res)) return res; + // put a space in it so layout will draw the list item + // XXX - revisit when layout is fixed + res = selection->Collapse(newItem,0); + if (NS_FAILED(res)) return res; + nsAutoString theText(" "); + res = InsertText(theText); + if (NS_FAILED(res)) return res; + // reposition selection to before the space character + res = GetStartNodeAndOffset(selection, &node, &offset); + if (NS_FAILED(res)) return res; + res = selection->Collapse(node,0); + if (NS_FAILED(res)) return res; } + return res; } -NS_IMETHODIMP -nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRange *aRange) -{ - if (!aRange) { return NS_ERROR_NULL_POINTER; } - nsresult res; - nsCOMPtrstartParent; - res = aRange->GetStartParent(getter_AddRefs(startParent)); - if ((NS_SUCCEEDED(res)) && startParent) - { - nsCOMPtrparentNode; - nsCOMPtrparentElement; - res = startParent->GetParentNode(getter_AddRefs(parentNode)); - while ((NS_SUCCEEDED(res)) && parentNode) - { - parentElement = do_QueryInterface(parentNode); - nsAutoString parentTag; - parentElement->GetTagName(parentTag); - PRBool isRoot; - IsRootTag(parentTag, isRoot); - if (aParentTag.EqualsIgnoreCase(parentTag)) - { - // go through list backwards so deletes don't interfere with the iteration - nsCOMPtr childNodes; - res = parentElement->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(res)) && (childNodes)) - { - nsCOMPtrgrandParent; - parentElement->GetParentNode(getter_AddRefs(grandParent)); - PRInt32 offsetInParent; - res = GetChildOffset(parentElement, grandParent, offsetInParent); - PRUint32 childCount; - childNodes->GetLength(&childCount); - PRInt32 i=childCount-1; - for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) - { - nsCOMPtr childNode; - res = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(res)) && (childNode)) - { - res = nsTextEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); - } - } - } - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::DeleteNode(parentElement); - } - } - break; - } - else if (PR_TRUE==isRoot) { // hit a local root node, terminate loop - break; - } - res = parentElement->GetParentNode(getter_AddRefs(parentNode)); - } - } - return res; -} - - // TODO: Implement "outdent" NS_IMETHODIMP nsHTMLEditor::Indent(const nsString& aIndent) @@ -1708,98 +1774,6 @@ nsHTMLEditor::Align(const nsString& aAlignType) } -NS_IMETHODIMP -nsHTMLEditor::InsertList(const nsString& aListType) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertList(aListType); -#endif // ENABLE_JS_EDITOR_LOG - - nsresult res; - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - PRBool cancel= PR_FALSE; - - nsAutoEditBatch beginBatching(this); - - // pre-process - nsEditor::GetSelection(getter_AddRefs(selection)); - nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeList); - if (aListType == "ol") ruleInfo.bOrdered = PR_TRUE; - else ruleInfo.bOrdered = PR_FALSE; - res = mRules->WillDoAction(selection, &ruleInfo, &cancel); - if (cancel || (NS_FAILED(res))) return res; - - // Find out if the selection is collapsed: - if (NS_FAILED(res) || !selection) return res; - - PRBool isCollapsed; - res = selection->GetIsCollapsed(&isCollapsed); - if (NS_FAILED(res)) return res; - - nsCOMPtr node; - PRInt32 offset; - - res = GetStartNodeAndOffset(selection, &node, &offset); - if (!node) res = NS_ERROR_FAILURE; - if (NS_FAILED(res)) return res; - - if (isCollapsed) - { - // have to find a place to put the list - nsCOMPtr parent = node; - nsCOMPtr topChild = node; - nsCOMPtr tmp; - - while ( !CanContainTag(parent, aListType)) - { - parent->GetParentNode(getter_AddRefs(tmp)); - if (!tmp) return NS_ERROR_FAILURE; - topChild = parent; - parent = tmp; - } - - if (parent != node) - { - // we need to split up to the child of parent - res = SplitNodeDeep(topChild, node, offset); - if (NS_FAILED(res)) return res; - // topChild already went to the right on the split - // so we don't need to add one to offset when figuring - // out where to plop list - offset = GetIndexOf(parent,topChild); - } - - // make a list - nsCOMPtr newList; - res = CreateNode(aListType, parent, offset, getter_AddRefs(newList)); - if (NS_FAILED(res)) return res; - // make a list item - nsAutoString tag("li"); - nsCOMPtr newItem; - res = CreateNode(tag, newList, 0, getter_AddRefs(newItem)); - if (NS_FAILED(res)) return res; - // put a space in it so layout will draw the list item - // XXX - revisit when layout is fixed - res = selection->Collapse(newItem,0); - if (NS_FAILED(res)) return res; - nsAutoString theText(" "); - res = InsertText(theText); - if (NS_FAILED(res)) return res; - // reposition selection to before the space character - res = GetStartNodeAndOffset(selection, &node, &offset); - if (NS_FAILED(res)) return res; - res = selection->Collapse(node,0); - if (NS_FAILED(res)) return res; - } - - return res; -} - NS_IMETHODIMP nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn) { @@ -1887,7 +1861,6 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * return NS_OK; } - NS_IMETHODIMP nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn) { @@ -2208,144 +2181,6 @@ nsHTMLEditor::CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement* return res; } -NS_IMETHODIMP -nsHTMLEditor::InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertElement(aElement, aDeleteSelection); -#endif // ENABLE_JS_EDITOR_LOG - - nsresult res = NS_ERROR_NOT_INITIALIZED; - - if (!aElement) - return NS_ERROR_NULL_POINTER; - - nsAutoEditBatch beginBatching(this); - // For most elements, set caret after inserting - //PRBool setCaretAfterElement = PR_TRUE; - - nsCOMPtrselection; - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (!NS_SUCCEEDED(res) || !selection) - return NS_ERROR_FAILURE; - - // Clear current selection. - // Should put caret at anchor point? - if (!aDeleteSelection) - { - PRBool collapseAfter = PR_TRUE; - // Named Anchor is a special case, - // We collapse to insert element BEFORE the selection - // For all other tags, we insert AFTER the selection - nsCOMPtr node = do_QueryInterface(aElement); - if (IsNamedAnchorNode(node)) - collapseAfter = PR_FALSE; - - if (collapseAfter) - { - // Default behavior is to collapse to the end of the selection - selection->ClearSelection(); - } else { - // Collapse to the start of the selection, - // We must explore the first range and find - // its parent and starting offset of selection - // TODO: Move this logic to a new method nsIDOMSelection::CollapseToStart()??? - nsCOMPtr firstRange; - res = selection->GetRangeAt(0, getter_AddRefs(firstRange)); - if (NS_SUCCEEDED(res) && firstRange) - { - nsCOMPtr parent; - res = firstRange->GetCommonParent(getter_AddRefs(parent)); - if (NS_SUCCEEDED(res) && parent) - { - PRInt32 startOffset; - firstRange->GetStartOffset(&startOffset); - selection->Collapse(parent, startOffset); - } else { - // Very unlikely, but collapse to the end if we failed above - selection->ClearSelection(); - } - } - } - } - - nsCOMPtr parentSelectedNode; - PRInt32 offsetForInsert; - res = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); - if (NS_SUCCEEDED(res) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetForInsert)) && parentSelectedNode) - { -#ifdef DEBUG_cmanske - { - nsAutoString name; - parentSelectedNode->GetNodeName(name); - printf("InsertElement: Anchor node of selection: "); - wprintf(name.GetUnicode()); - printf(" Offset: %d\n", offsetForInsert); - } -#endif - nsAutoString tagName; - aElement->GetNodeName(tagName); - tagName.ToLowerCase(); - nsCOMPtr parent = parentSelectedNode; - nsCOMPtr topChild = parentSelectedNode; - nsCOMPtr tmp; - nsAutoString parentTagName; - PRBool isRoot; - - // Search up the parent chain to find a suitable container - while (!CanContainTag(parent, tagName)) - { - // If the current parent is a root (body or table cell) - // then go no further - we can't insert - parent->GetNodeName(parentTagName); - res = IsRootTag(parentTagName, isRoot); - if (!NS_SUCCEEDED(res) || isRoot) - return NS_ERROR_FAILURE; - // Get the next parent - parent->GetParentNode(getter_AddRefs(tmp)); - if (!tmp) - return NS_ERROR_FAILURE; - topChild = parent; - parent = tmp; - } -#ifdef DEBUG_cmanske - { - nsAutoString name; - parent->GetNodeName(name); - printf("Parent node to insert under: "); - wprintf(name.GetUnicode()); - printf("\n"); - topChild->GetNodeName(name); - printf("TopChild to split: "); - wprintf(name.GetUnicode()); - printf("\n"); - } -#endif - if (parent != topChild) - { - // we need to split some levels above the original selection parent - res = SplitNodeDeep(topChild, parentSelectedNode, offsetForInsert); - if (NS_FAILED(res)) - return res; - // topChild went to the right on the split - // so this is the offset to insert at - offsetForInsert = GetIndexOf(parent,topChild); - } - // Now we can insert the new node - res = InsertNode(aElement, parent, offsetForInsert); - - // Set caret after element, but check for special case - // of inserting table-related elements: set in first cell instead - if (!SetCaretInTableCell(aElement)) - res = SetCaretAfterElement(aElement); - - } - return res; -} - NS_IMETHODIMP nsHTMLEditor::SaveHLineSettings(nsIDOMElement* aElement) { @@ -2401,7 +2236,7 @@ nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) // DON'T RETURN EXCEPT AT THE END -- WE NEED TO RELEASE THE aAnchorElement if (!aAnchorElement) - goto DELETE_ANCHOR; + goto DELETE_ANCHOR; // DON'T USE GOTO IN C++! // We must have a real selection res = GetSelection(getter_AddRefs(selection)); @@ -2427,7 +2262,7 @@ nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) { nsAutoEditBatch beginBatching(this); const nsString attribute("href"); - SetTextProperty(nsIEditProperty::a, &attribute, &href); + SetInlineProperty(nsIEditProperty::a, &attribute, &href); //TODO: Enumerate through other properties of the anchor tag // and set those as well. // Optimization: Modify SetTextProperty to set all attributes at once? @@ -2443,193 +2278,579 @@ DELETE_ANCHOR: return res; } -PRBool nsHTMLEditor::IsElementInBody(nsIDOMElement* aElement) -{ - if ( aElement ) - { - nsIDOMElement* bodyElement = nsnull; - nsresult res = nsEditor::GetBodyElement(&bodyElement); - if (NS_SUCCEEDED(res) && bodyElement) - { - nsCOMPtr parent; - nsCOMPtr currentElement = do_QueryInterface(aElement); - if (currentElement) - { - do { - currentElement->GetParentNode(getter_AddRefs(parent)); - if (parent) - { - if (parent == bodyElement) - return PR_TRUE; - currentElement = parent; - } - } while(parent); - } - } +NS_IMETHODIMP nsHTMLEditor::SetBackgroundColor(const nsString& aColor) +{ +// nsresult result; + NS_PRECONDITION(mDoc, "Missing Editor DOM Document"); + + // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level + // For initial testing, just set the background on the BODY tag (the document's background) + +// Do this only if setting a table or cell background +// It will be called in nsTextEditor::SetBackgroundColor for the page background +#if 0 //def ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SetBackgroundColor(aColor); +#endif // ENABLE_JS_EDITOR_LOG + + // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level + // For initial testing, just set the background on the BODY tag (the document's background) + + // Set the background color attribute on the body tag + nsCOMPtr bodyElement; + nsresult res = nsEditor::GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res) && bodyElement) + { + nsAutoEditBatch beginBatching(this); + bodyElement->SetAttribute("bgcolor", aColor); } - return PR_FALSE; + + return res; } -NS_IMETHODIMP -nsHTMLEditor::SelectElement(nsIDOMElement* aElement) +NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsString& aAttribute, const nsString& aValue) { + #ifdef ENABLE_JS_EDITOR_LOG nsAutoJSEditorLogLock logLock(mJSEditorLog); if (mJSEditorLog) - mJSEditorLog->SelectElement(aElement); + mJSEditorLog->SetBodyAttribute(aAttribute, aValue); #endif // ENABLE_JS_EDITOR_LOG - nsresult res = NS_ERROR_NULL_POINTER; + nsresult res; + // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - // Must be sure that element is contained in the document body - if (IsElementInBody(aElement)) + NS_ASSERTION(mDoc, "Missing Editor DOM Document"); + + // Set the background color attribute on the body tag + nsCOMPtr bodyElement; + + res = nsEditor::GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res) && bodyElement) { - nsCOMPtr selection; - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(res) && selection) - { - nsCOMPtrparent; - res = aElement->GetParentNode(getter_AddRefs(parent)); - if (NS_SUCCEEDED(res) && parent) - { - PRInt32 offsetInParent; - res = GetChildOffset(aElement, parent, offsetInParent); - - if (NS_SUCCEEDED(res)) - { - // Collapse selection to just before desired element, - selection->Collapse(parent, offsetInParent); - // then extend it to just after - selection->Extend(parent, offsetInParent+1); - } - } - } + // Use the editor's method which goes through the transaction system + SetAttribute(bodyElement, aAttribute, aValue); } return res; } -PRBool -nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement) +/* +NS_IMETHODIMP nsHTMLEditor::MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection) { - PRBool caretIsSet = PR_FALSE; + return NS_ERROR_NOT_IMPLEMENTED; +} - if (aElement && IsElementInBody(aElement)) +NS_IMETHODIMP nsHTMLEditor::MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::ScrollUp(nsIAtom *aIncrement) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::ScrollDown(nsIAtom *aIncrement) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::ScrollIntoView(PRBool aScrollToBegin) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +*/ + + + +NS_IMETHODIMP +nsHTMLEditor::GetDocumentLength(PRInt32 *aCount) +{ + if (!aCount) { return NS_ERROR_NULL_POINTER; } + nsresult result; + // initialize out params + *aCount = 0; + + nsCOMPtr sel; + result = GetSelection(getter_AddRefs(sel)); + if ((NS_SUCCEEDED(result)) && sel) { - nsresult res = NS_OK; - nsAutoString tagName; - aElement->GetNodeName(tagName); - tagName.ToLowerCase(); - if (tagName == "table" || tagName == "tr" || - tagName == "td" || tagName == "th" || - tagName == "thead" || tagName == "tfoot" || - tagName == "tbody" || tagName == "caption") + nsAutoSelectionReset selectionResetter(sel); + result = SelectAll(); + if (NS_SUCCEEDED(result)) { - nsCOMPtr node = do_QueryInterface(aElement); - nsCOMPtr parent; - // This MUST succeed if IsElementInBody was TRUE - node->GetParentNode(getter_AddRefs(parent)); - nsCOMPtrfirstChild; - // Find deepest child - PRBool hasChild; - while (NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild) + PRInt32 start, end; + result = GetTextSelectionOffsets(sel, start, end); + if (NS_SUCCEEDED(result)) { - if (NS_SUCCEEDED(node->GetFirstChild(getter_AddRefs(firstChild)))) - { - parent = node; - node = firstChild; + NS_ASSERTION(0==start, "GetTextSelectionOffsets failed to set start correctly."); + NS_ASSERTION(0<=end, "GetTextSelectionOffsets failed to set end correctly."); + if (0<=end) { + *aCount = end; } } - PRInt32 offset = 0; - nsCOMPtrlastChild; - res = parent->GetLastChild(getter_AddRefs(lastChild)); - if (NS_SUCCEEDED(res) && lastChild && node != lastChild) - { - if (node == lastChild) - { - // Check if node is text and has more than just a   - nsCOMPtrtextNode = do_QueryInterface(node); - nsString text; - char nbspStr[2] = {nbsp, 0}; - if (textNode && textNode->GetData(text)) - { - // Set selection relative to the text node - parent = node; - PRInt32 len = text.Length(); - if (len > 1 || text != nbspStr) - { - offset = len; - } - } - } else { - // We have > 1 node, so set to end of content - } - } - // Set selection at beginning of deepest node - // Should we set - nsCOMPtr selection; - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(res) && selection) - { - res = selection->Collapse(parent, offset); - if (NS_SUCCEEDED(res)) - caretIsSet = PR_TRUE; - } } } - return caretIsSet; -} + return result; +} -NS_IMETHODIMP -nsHTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement) +NS_IMETHODIMP nsHTMLEditor::SetMaxTextLength(PRInt32 aMaxTextLength) { -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); + mMaxTextLength = aMaxTextLength; + return NS_OK; +} - if (mJSEditorLog) - mJSEditorLog->SetCaretAfterElement(aElement); -#endif // ENABLE_JS_EDITOR_LOG +NS_IMETHODIMP nsHTMLEditor::GetMaxTextLength(PRInt32& aMaxTextLength) +{ + aMaxTextLength = mMaxTextLength; + return NS_OK; +} - nsresult res = NS_ERROR_NULL_POINTER; - // Be sure the element is contained in the document body - if (aElement && IsElementInBody(aElement)) - { - nsCOMPtr selection; - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(res) && selection) - { - nsCOMPtrparent; - res = aElement->GetParentNode(getter_AddRefs(parent)); - if (NS_SUCCEEDED(res) && parent) - { - PRInt32 offsetInParent; - res = GetChildOffset(aElement, parent, offsetInParent); - if (NS_SUCCEEDED(res)) - { - // Collapse selection to just after desired element, - selection->Collapse(parent, offsetInParent+1); -#ifdef DEBUG_cmanske - { - nsAutoString name; - parent->GetNodeName(name); - printf("SetCaretAfterElement: Parent node: "); - wprintf(name.GetUnicode()); - printf(" Offset: %d\n\nHTML:\n", offsetInParent+1); - nsString Format("text/html"); - nsString ContentsAs; - OutputToString(ContentsAs, Format, 2); - wprintf(ContentsAs.GetUnicode()); - } +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorStyleSheets methods --- +#pragma mark - #endif + + +NS_IMETHODIMP +nsHTMLEditor::AddStyleSheet(nsICSSStyleSheet* aSheet) +{ + AddStyleSheetTxn* aTxn; + nsresult rv = CreateTxnForAddStyleSheet(aSheet, &aTxn); + if (NS_SUCCEEDED(rv) && aTxn) + { + rv = Do(aTxn); + if (NS_SUCCEEDED(rv)) + { + mLastStyleSheet = do_QueryInterface(aSheet); // save it so we can remove before applying the next one + } + } + + return rv; +} + + +NS_IMETHODIMP +nsHTMLEditor::RemoveStyleSheet(nsICSSStyleSheet* aSheet) +{ + RemoveStyleSheetTxn* aTxn; + nsresult rv = CreateTxnForRemoveStyleSheet(aSheet, &aTxn); + if (NS_SUCCEEDED(rv) && aTxn) + { + rv = Do(aTxn); + if (NS_SUCCEEDED(rv)) + { + mLastStyleSheet = nsnull; // forget it + } + } + + return rv; +} + + +NS_IMETHODIMP nsHTMLEditor::ApplyStyleSheet(const nsString& aURL) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->ApplyStyleSheet(aURL); +#endif // ENABLE_JS_EDITOR_LOG + + // XXX: Note that this is not an undo-able action yet! + + nsresult rv = NS_OK; + nsIURI* uaURL = 0; + +#ifndef NECKO + rv = NS_NewURL(&uaURL, aURL); +#else + rv = NS_NewURI(&uaURL, aURL); +#endif // NECKO + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr document; + + rv = mPresShell->GetDocument(getter_AddRefs(document)); + + if (NS_SUCCEEDED(rv)) { + if (document) { + nsCOMPtr container = do_QueryInterface(document); + + if (container) { + nsICSSLoader *cssLoader = 0; + nsICSSStyleSheet *cssStyleSheet = 0; + + rv = container->GetCSSLoader(cssLoader); + + if (NS_SUCCEEDED(rv)) { + if (cssLoader) { + PRBool complete; + + rv = cssLoader->LoadAgentSheet(uaURL, cssStyleSheet, complete, + ApplyStyleSheetToPresShellDocument, + this); + + if (NS_SUCCEEDED(rv)) { + if (complete) { + if (cssStyleSheet) { + ApplyStyleSheetToPresShellDocument(cssStyleSheet, + this); + } + else + rv = NS_ERROR_NULL_POINTER; + } + + // + // If not complete, we will be notified later + // with a call to AddStyleSheetToEditorDocument(). + // + } + } + else + rv = NS_ERROR_NULL_POINTER; + } } + else + rv = NS_ERROR_NULL_POINTER; } + else + rv = NS_ERROR_NULL_POINTER; } + + NS_RELEASE(uaURL); } + + return rv; +} + + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorMailSupport methods --- +#pragma mark - +#endif + +// +// Get the wrap width for the first PRE tag in the document. +// If no PRE tag, throw an error. +// +NS_IMETHODIMP nsHTMLEditor::GetBodyWrapWidth(PRInt32 *aWrapColumn) +{ + nsresult res; + + if (! aWrapColumn) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr preElement = FindPreElement(); + if (!preElement) + return NS_ERROR_UNEXPECTED; + nsString colsStr ("cols"); + nsString numCols; + PRBool isSet; + res = GetAttributeValue(preElement, colsStr, numCols, isSet); + if (!NS_SUCCEEDED(res)) + return NS_ERROR_UNEXPECTED; + + if (isSet) + { + PRInt32 errCode; + *aWrapColumn = numCols.ToInteger(&errCode); + if (errCode) + return NS_ERROR_FAILURE; + return NS_OK; + } + + // if we get here, cols isn't set, so check whether wrap is set: + nsString wrapStr ("wrap"); + res = GetAttributeValue(preElement, colsStr, numCols, isSet); + if (!NS_SUCCEEDED(res)) + return NS_ERROR_UNEXPECTED; + + if (isSet) + *aWrapColumn = 0; // wrap to window width + else + *aWrapColumn = -1; // no wrap + + return NS_OK; +} + +// +// Change the wrap width on the first
 tag in this document.
+// (Eventually want to search for more than one in case there are
+// interspersed quoted text blocks.)
+// 
+NS_IMETHODIMP nsHTMLEditor::SetBodyWrapWidth(PRInt32 aWrapColumn)
+{
+  nsresult res;
+
+  mWrapColumn = aWrapColumn;
+
+  nsCOMPtr preElement = FindPreElement();
+  if (!preElement)
+    return NS_ERROR_UNEXPECTED;
+
+  nsString wrapStr ("wrap");
+  nsString colsStr ("cols");
+
+  // If wrap col is nonpositive, then we need to remove any existing "cols":
+  if (aWrapColumn <= 0)
+  {
+    (void)RemoveAttribute(preElement, colsStr);
+
+    if (aWrapColumn == 0)        // Wrap to window width
+    {
+      nsString oneStr ("1");
+      res = SetAttribute(preElement, wrapStr, oneStr);
+    }
+    else res = NS_OK;
+    return res;
+  }
+
+  // Otherwise we're setting cols, want to remove wrap
+  (void)RemoveAttribute(preElement, wrapStr);
+  nsString numCols;
+  numCols.Append(aWrapColumn, 10);
+  res = SetAttribute(preElement, colsStr, numCols);
+
+  // Layout doesn't detect that this attribute change requires redraw.  Sigh.
+  //HACKForceRedraw();  // This doesn't do it either!
+
+  return res;
+}  
+
+
+
+// 
+// HTML PasteAsQuotation: Paste in a blockquote type=cite
+//
+NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->PasteAsQuotation();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsAutoString citation("");
+  return PasteAsCitedQuotation(citation);
+}
+
+NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation)
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->PasteAsCitedQuotation(aCitation);
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsAutoEditBatch beginBatching(this);
+  nsCOMPtr newNode;
+  nsAutoString tag("blockquote");
+  nsresult res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+  if (NS_FAILED(res) || !newNode)
+    return res;
+
+  // Try to set type=cite.  Ignore it if this fails.
+  nsCOMPtr newElement (do_QueryInterface(newNode));
+  if (newElement)
+  {
+    nsAutoString type ("type");
+    nsAutoString cite ("cite");
+    newElement->SetAttribute(type, cite);
+  }
+
+  // Set the selection to the underneath the node we just inserted:
+  nsCOMPtr selection;
+  res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res) || !selection)
+  {
+#ifdef DEBUG_akkana
+    printf("Can't get selection!");
+#endif
+  }
+
+  res = selection->Collapse(newNode, 0);
+  if (NS_FAILED(res))
+  {
+#ifdef DEBUG_akkana
+    printf("Couldn't collapse");
+#endif
+  }
+
+  res = Paste();
   return res;
 }
 
+//
+// Paste a plaintext quotation
+//
+NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->PasteAsQuotation();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  // Get Clipboard Service
+  nsIClipboard* clipboard;
+  nsresult rv = nsServiceManager::GetService(kCClipboardCID,
+                                             nsIClipboard::GetIID(),
+                                             (nsISupports **)&clipboard);
+
+  // Create generic Transferable for getting the data
+  nsCOMPtr trans;
+  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
+                                          nsITransferable::GetIID(), 
+                                          (void**) getter_AddRefs(trans));
+  if (NS_SUCCEEDED(rv) && trans)
+  {
+    // We only handle plaintext pastes here
+    nsAutoString flavor(kTextMime);
+    trans->AddDataFlavor(&flavor);
+
+      // Get the Data from the clipboard
+    clipboard->GetData(trans);
+
+    // Now we ask the transferable for the data
+    // it still owns the data, we just have a pointer to it.
+    // If it can't support a "text" output of the data the call will fail
+    char *str = 0;
+    PRUint32 len;
+    rv = trans->GetTransferData(&flavor, (void **)&str, &len);
+    if (NS_SUCCEEDED(rv) && str && len > 0)
+    {
+      nsString stuffToPaste;
+      stuffToPaste.SetString(str, len);
+      rv = InsertAsPlaintextQuotation(stuffToPaste);
+    }
+  }
+  nsServiceManager::ReleaseService(kCClipboardCID, clipboard);
+
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText)
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->InsertAsQuotation(aQuotedText);
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsAutoString citation ("");
+  return InsertAsCitedQuotation(aQuotedText, citation);
+}
+
+// text insert.
+NS_IMETHODIMP nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText)
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->InsertAsQuotation(aQuotedText);
+#endif // ENABLE_JS_EDITOR_LOG
+
+  // Now we have the text.  Cite it appropriately:
+  nsCOMPtr citer;
+  nsCOMPtr prefs;
+  nsresult rv = nsServiceManager::GetService(kPrefServiceCID,
+                                             nsIPref::GetIID(),
+                                             (nsISupports**)&prefs);
+  char *citationType = 0;
+  rv = prefs->CopyCharPref("mail.compose.citationType", &citationType);
+                          
+  if (NS_SUCCEEDED(rv) && citationType[0])
+  {
+    if (!strncmp(citationType, "aol", 3))
+      citer = new nsAOLCiter;
+    else
+      citer = new nsInternetCiter;
+    PL_strfree(citationType);
+  }
+  else
+    citer = new nsInternetCiter;
+  
+  nsServiceManager::ReleaseService(kPrefServiceCID, prefs);
+
+  // Let the citer quote it for us:
+  nsString quotedStuff;
+  rv = citer->GetCiteString(aQuotedText, quotedStuff);
+  if (!NS_SUCCEEDED(rv))
+    return rv;
+
+  // Insert blank lines after the quoted text:
+  quotedStuff += "\n\n";
+
+  return InsertText(quotedStuff);
+}
+
+NS_IMETHODIMP nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
+                                                   const nsString& aCitation)
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->InsertAsCitedQuotation(aQuotedText, aCitation);
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsAutoEditBatch beginBatching(this);
+  nsCOMPtr newNode;
+  nsAutoString tag("blockquote");
+  nsresult res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+  if (NS_FAILED(res) || !newNode)
+    return res;
+
+  // Try to set type=cite.  Ignore it if this fails.
+  nsCOMPtr newElement (do_QueryInterface(newNode));
+  if (newElement)
+  {
+    nsAutoString type ("type");
+    nsAutoString cite ("cite");
+    newElement->SetAttribute(type, cite);
+
+    if (aCitation.Length() > 0)
+      newElement->SetAttribute(cite, aCitation);
+  }
+
+  res = InsertHTML(aQuotedText);
+  return res;
+}
+
+
 NS_IMETHODIMP
 nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
 {
@@ -2702,6 +2923,1368 @@ nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
   return res;
 }
 
+
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark --- nsIEditor overrides ---
+#pragma mark -
+#endif
+
+NS_IMETHODIMP nsHTMLEditor::Cut()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->Cut();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsCOMPtr selection;
+  nsresult res = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection));
+  if (!NS_SUCCEEDED(res))
+    return res;
+
+  PRBool isCollapsed;
+  if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  res = Copy();
+  if (NS_SUCCEEDED(res))
+    res = DeleteSelection(eDoNothing);
+  return res;
+}
+
+NS_IMETHODIMP nsHTMLEditor::Copy()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->Copy();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  //printf("nsEditor::Copy\n");
+
+  return mPresShell->DoCopy();
+}
+
+NS_IMETHODIMP nsHTMLEditor::Paste()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->Paste();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsString stuffToPaste;
+
+  // Get Clipboard Service
+  nsIClipboard* clipboard;
+  nsresult rv = nsServiceManager::GetService(kCClipboardCID,
+                                             nsIClipboard::GetIID(),
+                                             (nsISupports **)&clipboard);
+
+  // Create generic Transferable for getting the data
+  nsCOMPtr trans;
+  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
+                                          nsITransferable::GetIID(), 
+                                          (void**) getter_AddRefs(trans));
+  if (NS_SUCCEEDED(rv))
+  {
+    // Get the nsITransferable interface for getting the data from the clipboard
+    if (trans)
+    {
+      // Create the desired DataFlavor for the type of data
+      // we want to get out of the transferable
+      nsAutoString imageFlavor(kJPEGImageMime);
+      nsAutoString htmlFlavor(kHTMLMime);
+      nsAutoString textFlavor(kTextMime);
+
+      if ((mFlags & eEditorPlaintextMask) == 0)  // This should only happen in html editors, not plaintext
+      {
+        trans->AddDataFlavor(&imageFlavor);
+        trans->AddDataFlavor(&htmlFlavor);
+      }
+      trans->AddDataFlavor(&textFlavor);
+
+      // Get the Data from the clipboard
+      if (NS_SUCCEEDED(clipboard->GetData(trans)))
+      {
+        nsAutoString flavor;
+        char *       data;
+        PRUint32     len;
+        if (NS_SUCCEEDED(trans->GetAnyTransferData(&flavor, (void **)&data, &len)))
+        {
+#ifdef DEBUG_akkana
+          printf("Got flavor [%s]\n", flavor.ToNewCString());
+#endif
+          if (flavor.Equals(htmlFlavor))
+          {
+            if (data && len > 0) // stuffToPaste is ready for insertion into the content
+            {
+              stuffToPaste.SetString(data, len);
+              rv = InsertHTML(stuffToPaste);
+            }
+          }
+          else if (flavor.Equals(textFlavor))
+          {
+            if (data && len > 0) // stuffToPaste is ready for insertion into the content
+            {
+              stuffToPaste.SetString(data, len);
+              rv = InsertText(stuffToPaste);
+            }
+          }
+          else if (flavor.Equals(imageFlavor))
+          {
+            // Insert Image code here
+            printf("Don't know how to insert an image yet!\n");
+            //nsIImage* image = (nsIImage *)data;
+            //NS_RELEASE(image);
+            rv = NS_ERROR_FAILURE; // for now give error code
+          }
+        }
+
+      }
+    }
+  }
+  nsServiceManager::ReleaseService(kCClipboardCID, clipboard);
+
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::OutputToString(nsString& aOutputString,
+                                           const nsString& aFormatType,
+                                           PRUint32 aFlags)
+{
+  PRBool cancel;
+  nsString resultString;
+  nsTextRulesInfo ruleInfo(nsTextEditRules::kOutputText);
+  ruleInfo.outString = &resultString;
+  nsresult rv = mRules->WillDoAction(nsnull, &ruleInfo, &cancel);
+  if (NS_FAILED(rv)) { return rv; }
+  if (PR_TRUE==cancel)
+  { // this case will get triggered by password fields
+    aOutputString = *(ruleInfo.outString);
+  }
+  else
+  { // default processing
+    nsCOMPtr encoder;
+    char* progid = new char[strlen(NS_DOC_ENCODER_PROGID_BASE) + aFormatType.Length() + 1];
+    if (! progid)
+      return NS_ERROR_OUT_OF_MEMORY;
+    strcpy(progid, NS_DOC_ENCODER_PROGID_BASE);
+    char* type = aFormatType.ToNewCString();
+    strcat(progid, type);
+    delete[] type;
+    rv = nsComponentManager::CreateInstance(progid,
+                                            nsnull,
+                                            nsIDocumentEncoder::GetIID(),
+                                            getter_AddRefs(encoder));
+
+    delete[] progid;
+    if (NS_FAILED(rv))
+    {
+      printf("Couldn't get progid %s\n", progid);
+      return rv;
+    }
+
+    nsCOMPtr domdoc;
+    rv = GetDocument(getter_AddRefs(domdoc));
+    if (NS_FAILED(rv))
+      return rv;
+    nsCOMPtr doc = do_QueryInterface(domdoc);
+
+    nsCOMPtr shell;
+
+ 	  rv = GetPresShell(getter_AddRefs(shell));
+    if (NS_FAILED(rv))
+      return rv;
+    rv = encoder->Init(shell, doc, aFormatType);
+    if (NS_FAILED(rv))
+      return rv;
+
+	  if (aFlags & EditorOutputSelectionOnly)
+    {
+	    nsCOMPtr selection;
+	    rv = GetSelection(getter_AddRefs(selection));
+	    if (NS_SUCCEEDED(rv) && selection)
+	      encoder->SetSelection(selection);
+	  }
+	  
+    // Try to set pretty printing, but don't panic if it doesn't work:
+    (void)encoder->PrettyPrint((aFlags & EditorOutputFormatted)
+                               ? PR_TRUE : PR_FALSE);
+    // Indicate whether we want the comment and doctype headers prepended:
+    (void)encoder->AddHeader((aFlags & EditorOutputNoDoctype)
+                             ? PR_FALSE : PR_TRUE);
+    // Set the wrap column.  If our wrap column is 0,
+    // i.e. wrap to body width, then don't set it, let the
+    // document encoder use its own default.
+    if (mWrapColumn != 0)
+    {
+      PRUint32 wc;
+      if (mWrapColumn < 0)
+        wc = 0;
+      else
+        wc = (PRUint32)mWrapColumn;
+      if (mWrapColumn > 0)
+        (void)encoder->SetWrapColumn(wc);
+    }
+
+    rv = encoder->EncodeToString(aOutputString);
+  }
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::OutputToStream(nsIOutputStream* aOutputStream,
+                                           const nsString& aFormatType,
+                                           const nsString* aCharset,
+                                           PRUint32 aFlags)
+{
+  nsresult rv;
+  nsCOMPtr encoder;
+  char* progid = new char[strlen(NS_DOC_ENCODER_PROGID_BASE) + aFormatType.Length() + 1];
+  if (! progid)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+  strcpy(progid, NS_DOC_ENCODER_PROGID_BASE);
+  char* type = aFormatType.ToNewCString();
+  strcat(progid, type);
+  delete[] type;
+  rv = nsComponentManager::CreateInstance(progid,
+                                          nsnull,
+                                          nsIDocumentEncoder::GetIID(),
+                                          getter_AddRefs(encoder));
+
+  delete[] progid;
+  if (NS_FAILED(rv))
+  {
+    printf("Couldn't get progid %s\n", progid);
+    return rv;
+  }
+
+  nsCOMPtr domdoc;
+  rv = GetDocument(getter_AddRefs(domdoc));
+  if (NS_FAILED(rv))
+    return rv;
+  nsCOMPtr doc = do_QueryInterface(domdoc);
+
+  if (aCharset && aCharset->Length() != 0 && aCharset->Equals("null")==PR_FALSE)
+    encoder->SetCharset(*aCharset);
+
+  nsCOMPtr shell;
+
+ 	rv = GetPresShell(getter_AddRefs(shell));
+  if (NS_SUCCEEDED(rv)) {
+    rv = encoder->Init(shell,doc, aFormatType);
+    if (NS_FAILED(rv))
+      return rv;
+  }
+
+  if (aFlags & EditorOutputSelectionOnly)
+  {
+    nsCOMPtr  selection;
+    rv = GetSelection(getter_AddRefs(selection));
+    if (NS_SUCCEEDED(rv) && selection)
+      encoder->SetSelection(selection);
+  }
+
+  // Try to set pretty printing, but don't panic if it doesn't work:
+  (void)encoder->PrettyPrint((aFlags & EditorOutputFormatted)
+                             ? PR_TRUE : PR_FALSE);
+  // Indicate whether we want the comment and doc type headers prepended:
+  (void)encoder->AddHeader((aFlags & EditorOutputNoDoctype)
+                           ? PR_FALSE : PR_TRUE);
+  // Set the wrap column.  If our wrap column is 0,
+  // i.e. wrap to body width, then don't set it, let the
+  // document encoder use its own default.
+  if (mWrapColumn != 0)
+  {
+    PRUint32 wc;
+    if (mWrapColumn < 0)
+      wc = 0;
+    else
+      wc = (PRUint32)mWrapColumn;
+    if (mWrapColumn > 0)
+      (void)encoder->SetWrapColumn(wc);
+  }
+
+  return encoder->EncodeToStream(aOutputStream);
+}
+
+
+NS_IMETHODIMP
+nsHTMLEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
+{
+#ifdef DEBUG
+  if (!outNumTests || !outNumTestsFailed)
+    return NS_ERROR_NULL_POINTER;
+
+	TextEditorTest *tester = new TextEditorTest();
+	if (!tester)
+	  return NS_ERROR_OUT_OF_MEMORY;
+	 
+  tester->Run(this, outNumTests, outNumTestsFailed);
+  delete tester;
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark --- StyleSheet utils ---
+#pragma mark -
+#endif
+
+
+NS_IMETHODIMP
+nsHTMLEditor::ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet)
+{
+  nsresult  rv = NS_OK;
+  
+  BeginTransaction();
+
+  if (mLastStyleSheet)
+  {
+    rv = RemoveStyleSheet(mLastStyleSheet);
+  }
+
+  rv = AddStyleSheet(aNewSheet);
+  
+  EndTransaction();
+
+  return rv;
+}
+
+
+
+/* static callback */
+void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData)
+{
+  nsresult rv = NS_OK;
+
+  nsHTMLEditor *editor = NS_STATIC_CAST(nsHTMLEditor*, aData);
+  if (editor)
+  {
+    rv = editor->ReplaceStyleSheet(aSheet);
+  }
+  
+  // we lose the return value here. Set a flag in the editor?
+}
+
+
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark --- Random methods ---
+#pragma mark -
+#endif
+
+
+NS_IMETHODIMP nsHTMLEditor::GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject)
+{
+  nsresult result = NS_ERROR_FAILURE;  // we return an error unless we get the index
+  if( mPresShell != nsnull )
+  {
+    if ((nsnull!=aNode))
+    { // get the content interface
+      nsCOMPtr nodeAsContent( do_QueryInterface(aNode) );
+      if (nodeAsContent)
+      { // get the frame from the content interface
+        //Note: frames are not ref counted, so don't use an nsCOMPtr
+        *aLayoutObject = nsnull;
+        result = mPresShell->GetLayoutObjectFor(nodeAsContent, aLayoutObject);
+      }
+    }
+    else {
+      result = NS_ERROR_NULL_POINTER;
+    }
+  }
+  return result;
+}
+
+
+NS_IMETHODIMP
+nsHTMLEditor::SetTypeInStateForProperty(TypeInState    &aTypeInState, 
+                                        nsIAtom        *aPropName, 
+                                        const nsString *aAttribute,
+                                        const nsString *aValue)
+{
+  if (!aPropName) {
+    return NS_ERROR_NULL_POINTER;
+  }
+  PRUint32 propEnum;
+  aTypeInState.GetEnumForName(aPropName, propEnum);
+  if (nsIEditProperty::b==aPropName || nsIEditProperty::i==aPropName || nsIEditProperty::u==aPropName) 
+  {
+    if (PR_TRUE==aTypeInState.IsSet(propEnum))
+    { // toggle currently set boldness
+      aTypeInState.UnSet(propEnum);
+    }
+    else
+    { // get the current style and set boldness to the opposite of the current state
+      PRBool any = PR_FALSE;
+      PRBool all = PR_FALSE;
+      PRBool first = PR_FALSE;
+      GetInlineProperty(aPropName, aAttribute, nsnull, first, any, all); // operates on current selection
+      aTypeInState.SetProp(propEnum, !any);
+    }
+  }
+  else if (nsIEditProperty::font==aPropName)
+  {
+    if (!aAttribute) { return NS_ERROR_NULL_POINTER; }
+    nsIAtom *attribute = NS_NewAtom(*aAttribute);
+    if (!attribute) { return NS_ERROR_NULL_POINTER; }
+    PRUint32 attrEnum;
+    aTypeInState.GetEnumForName(attribute, attrEnum);
+    if (nsIEditProperty::color==attribute || nsIEditProperty::face==attribute || nsIEditProperty::size==attribute)
+    {
+      if (PR_TRUE==aTypeInState.IsSet(attrEnum))
+      { 
+        if (nsnull==aValue) {
+          aTypeInState.UnSet(attrEnum);
+        }
+        else { // we're just changing the value of color
+          aTypeInState.SetPropValue(attrEnum, *aValue);
+        }
+      }
+      else
+      { // get the current style and set font color if it's needed
+        if (!aValue) { return NS_ERROR_NULL_POINTER; }
+        PRBool any = PR_FALSE;
+        PRBool all = PR_FALSE;
+        PRBool first = PR_FALSE;
+        GetInlineProperty(aPropName, aAttribute, aValue, first, any, all); // operates on current selection
+        if (PR_FALSE==all) {
+          aTypeInState.SetPropValue(attrEnum, *aValue);
+        }
+      }    
+    }
+    else { return NS_ERROR_FAILURE; }
+  }
+  else { return NS_ERROR_FAILURE; }
+  return NS_OK;
+}
+
+
+// this will NOT find aAttribute unless aAttribute has a non-null value
+// so singleton attributes like  will not be matched!
+void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode     *aNode,
+                                              nsIAtom        *aProperty, 
+                                              const nsString *aAttribute, 
+                                              const nsString *aValue, 
+                                              PRBool         &aIsSet,
+                                              nsIDOMNode    **aStyleNode) const
+{
+  nsresult result;
+  aIsSet = PR_FALSE;  // must be initialized to false for code below to work
+  nsAutoString propName;
+  aProperty->ToString(propName);
+  nsCOMPtrparent;
+  result = aNode->GetParentNode(getter_AddRefs(parent));
+  while (NS_SUCCEEDED(result) && parent)
+  {
+    nsCOMPtrelement;
+    element = do_QueryInterface(parent);
+    if (element)
+    {
+      nsString tag;
+      element->GetTagName(tag);
+      if (propName.EqualsIgnoreCase(tag))
+      {
+        PRBool found = PR_FALSE;
+        if (aAttribute && 0!=aAttribute->Length())
+        {
+          nsAutoString value;
+          element->GetAttribute(*aAttribute, value);
+          if (0!=value.Length())
+          {
+            if (!aValue) {
+              found = PR_TRUE;
+            }
+            else if (aValue->EqualsIgnoreCase(value)) {
+              found = PR_TRUE;
+            }
+            else {  // we found the prop with the attribute, but the value doesn't match
+              break;
+            }
+          }
+        }
+        else { 
+          found = PR_TRUE;
+        }
+        if (PR_TRUE==found)
+        {
+          aIsSet = PR_TRUE;
+          break;
+        }
+      }
+    }
+    nsCOMPtrtemp;
+    result = parent->GetParentNode(getter_AddRefs(temp));
+    if (NS_SUCCEEDED(result) && temp) {
+      parent = do_QueryInterface(temp);
+    }
+    else {
+      parent = do_QueryInterface(nsnull);
+    }
+  }
+}
+
+void nsHTMLEditor::IsTextStyleSet(nsIStyleContext *aSC, 
+                                  nsIAtom *aProperty, 
+                                  const nsString *aAttribute,  
+                                  PRBool &aIsSet) const
+{
+  aIsSet = PR_FALSE;
+  if (aSC && aProperty)
+  {
+    nsStyleFont* font = (nsStyleFont*)aSC->GetStyleData(eStyleStruct_Font);
+    if (nsIEditProperty::i==aProperty)
+    {
+      aIsSet = PRBool(font->mFont.style & NS_FONT_STYLE_ITALIC);
+    }
+    else if (nsIEditProperty::b==aProperty)
+    { // XXX: check this logic with Peter
+      aIsSet = PRBool(font->mFont.weight > NS_FONT_WEIGHT_NORMAL);
+    }
+  }
+}
+
+
+// this is a complete ripoff from nsTextEditor::GetTextSelectionOffsetsForRange
+// the two should use common code, or even just be one method
+nsresult nsHTMLEditor::GetTextSelectionOffsets(nsIDOMSelection *aSelection,
+                                               PRInt32 &aStartOffset, 
+                                               PRInt32 &aEndOffset)
+{
+  nsresult result;
+  // initialize out params
+  aStartOffset = 0; // default to first char in selection
+  aEndOffset = -1;  // default to total length of text in selection
+
+  nsCOMPtr startNode, endNode, parentNode;
+  PRInt32 startOffset, endOffset;
+  aSelection->GetAnchorNode(getter_AddRefs(startNode));
+  aSelection->GetAnchorOffset(&startOffset);
+  aSelection->GetFocusNode(getter_AddRefs(endNode));
+  aSelection->GetFocusOffset(&endOffset);
+
+  nsCOMPtr enumerator;
+  result = aSelection->GetEnumerator(getter_AddRefs(enumerator));
+  if (NS_SUCCEEDED(result) && enumerator)
+  {
+    // don't use "result" in this block
+    enumerator->First(); 
+    nsCOMPtr currentItem;
+    nsresult findParentResult = enumerator->CurrentItem(getter_AddRefs(currentItem));
+    if ((NS_SUCCEEDED(findParentResult)) && (currentItem))
+    {
+      nsCOMPtr range( do_QueryInterface(currentItem) );
+      range->GetCommonParent(getter_AddRefs(parentNode));
+    }
+    else parentNode = do_QueryInterface(startNode);
+  }
+
+  nsCOMPtr iter;
+  result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
+                                              nsIContentIterator::GetIID(), 
+                                              getter_AddRefs(iter));
+  if ((NS_SUCCEEDED(result)) && iter)
+  {
+    PRUint32 totalLength=0;
+    nsCOMPtrtextNode;
+    nsCOMPtrblockParentContent = do_QueryInterface(parentNode);
+    iter->Init(blockParentContent);
+    // loop through the content iterator for each content node
+    nsCOMPtr content;
+    result = iter->CurrentNode(getter_AddRefs(content));
+    while (NS_COMFALSE == iter->IsDone())
+    {
+      textNode = do_QueryInterface(content);
+      if (textNode)
+      {
+        nsCOMPtrcurrentNode = do_QueryInterface(textNode);
+        if (!currentNode) {return NS_ERROR_NO_INTERFACE;}
+        if (PR_TRUE==IsEditable(currentNode))
+        {
+          if (currentNode.get() == startNode.get())
+          {
+            aStartOffset = totalLength + startOffset;
+          }
+          if (currentNode.get() == endNode.get())
+          {
+            aEndOffset = totalLength + endOffset;
+            break;
+          }
+          PRUint32 length;
+          textNode->GetLength(&length);
+          totalLength += length;
+        }
+      }
+      iter->Next();
+      iter->CurrentNode(getter_AddRefs(content));
+    }
+    if (-1==aEndOffset) {
+      aEndOffset = totalLength;
+    }
+  }
+  return result;
+}
+
+
+
+void nsHTMLEditor::GetTextSelectionOffsetsForRange(nsIDOMSelection *aSelection,
+                                                   nsIDOMNode **aParent,
+                                                   PRInt32     &aStartOffset, 
+                                                   PRInt32     &aEndOffset)
+{
+  nsresult result;
+
+  nsCOMPtr startNode, endNode;
+  PRInt32 startOffset, endOffset;
+  aSelection->GetAnchorNode(getter_AddRefs(startNode));
+  aSelection->GetAnchorOffset(&startOffset);
+  aSelection->GetFocusNode(getter_AddRefs(endNode));
+  aSelection->GetFocusOffset(&endOffset);
+
+  nsCOMPtr enumerator;
+  result = aSelection->GetEnumerator(getter_AddRefs(enumerator));
+  if (NS_SUCCEEDED(result) && enumerator)
+  {
+    enumerator->First(); 
+    nsCOMPtr currentItem;
+    result = enumerator->CurrentItem(getter_AddRefs(currentItem));
+    if ((NS_SUCCEEDED(result)) && currentItem)
+    {
+      nsCOMPtr range( do_QueryInterface(currentItem) );
+      range->GetCommonParent(aParent);
+    }
+  }
+
+  nsCOMPtr iter;
+  result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
+                                              nsIContentIterator::GetIID(), 
+                                              getter_AddRefs(iter));
+  if ((NS_SUCCEEDED(result)) && iter)
+  {
+    PRUint32 totalLength=0;
+    nsCOMPtrtextNode;
+    nsCOMPtrblockParentContent = do_QueryInterface(*aParent);
+    iter->Init(blockParentContent);
+    // loop through the content iterator for each content node
+    nsCOMPtr content;
+    result = iter->CurrentNode(getter_AddRefs(content));
+    while (NS_COMFALSE == iter->IsDone())
+    {
+      textNode = do_QueryInterface(content);
+      if (textNode)
+      {
+        nsCOMPtrcurrentNode = do_QueryInterface(textNode);
+        if (currentNode.get() == startNode.get())
+        {
+          aStartOffset = totalLength + startOffset;
+        }
+        if (currentNode.get() == endNode.get())
+        {
+          aEndOffset = totalLength + endOffset;
+          break;
+        }
+        PRUint32 length;
+        textNode->GetLength(&length);
+        totalLength += length;
+      }
+      iter->Next();
+      iter->CurrentNode(getter_AddRefs(content));
+    }
+  }
+}
+
+void nsHTMLEditor::ResetTextSelectionForRange(nsIDOMNode *aParent,
+                                              PRInt32     aStartOffset,
+                                              PRInt32     aEndOffset,
+                                              nsIDOMSelection *aSelection)
+{
+  nsCOMPtr startNode, endNode;
+  PRInt32 startOffset, endOffset;
+
+  nsresult result;
+  nsCOMPtr iter;
+  result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
+                                              nsIContentIterator::GetIID(), 
+                                              getter_AddRefs(iter));
+  if ((NS_SUCCEEDED(result)) && iter)
+  {
+    PRBool setStart = PR_FALSE;
+    PRUint32 totalLength=0;
+    nsCOMPtrtextNode;
+    nsCOMPtrblockParentContent = do_QueryInterface(aParent);
+    iter->Init(blockParentContent);
+    // loop through the content iterator for each content node
+    nsCOMPtr content;
+    result = iter->CurrentNode(getter_AddRefs(content));
+    while (NS_COMFALSE == iter->IsDone())
+    {
+      textNode = do_QueryInterface(content);
+      if (textNode)
+      {
+        PRUint32 length;
+        textNode->GetLength(&length);
+        if ((PR_FALSE==setStart) && aStartOffset<=(PRInt32)(totalLength+length))
+        {
+          setStart = PR_TRUE;
+          startNode = do_QueryInterface(textNode);
+          startOffset = aStartOffset-totalLength;
+        }
+        if (aEndOffset<=(PRInt32)(totalLength+length))
+        {
+          endNode = do_QueryInterface(textNode);
+          endOffset = aEndOffset-totalLength;
+          break;
+        }        
+        totalLength += length;
+      }
+      iter->Next();
+      iter->CurrentNode(getter_AddRefs(content));
+    }
+    aSelection->Collapse(startNode, startOffset);
+    aSelection->Extend(endNode, endOffset);
+  }
+}
+
+#pragma mark -
+
+//================================================================
+// HTML Editor methods
+//
+// Note: Table Editing methods are implemented in EditTable.cpp
+//
+
+NS_IMETHODIMP 
+nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, 
+                                    nsString &aParentTag,
+                                    BlockTransformationType aTransformation)
+{
+  if (!aNode) { return NS_ERROR_NULL_POINTER; }
+  if (gNoisy) 
+  { 
+    char *tag = aParentTag.ToNewCString();
+    printf("---------- ReParentContentOfNode(%p,%s,%d) -----------\n", aNode, tag, aTransformation); 
+    delete [] tag;
+  }
+  // find the current block parent, or just use aNode if it is a block node
+  nsCOMPtrblockParentElement;
+  nsCOMPtrnodeToReParent; // this is the node we'll operate on, by default it's aNode
+  nsresult res = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(nodeToReParent));
+  PRBool nodeIsInline;
+  PRBool nodeIsBlock=PR_FALSE;
+  IsNodeInline(aNode, nodeIsInline);
+  if (PR_FALSE==nodeIsInline)
+  {
+    nsresult QIResult;
+    nsCOMPtrnodeAsText;
+    QIResult = aNode->QueryInterface(nsIDOMCharacterData::GetIID(), getter_AddRefs(nodeAsText));
+    if (NS_FAILED(QIResult) || !nodeAsText) {
+      nodeIsBlock=PR_TRUE;
+    }
+  }
+  // if aNode is the block parent, then the node to reparent is one of its children
+  if (PR_TRUE==nodeIsBlock) 
+  {
+    res = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(blockParentElement));
+    if (NS_SUCCEEDED(res) && blockParentElement) {
+      res = aNode->GetFirstChild(getter_AddRefs(nodeToReParent));
+    }
+  }
+  else { // we just need to get the block parent of aNode
+    res = GetBlockParent(aNode, getter_AddRefs(blockParentElement));
+  }
+  // at this point, we must have a good res, a node to reparent, and a block parent
+  if (!nodeToReParent) { return NS_ERROR_UNEXPECTED;}
+  if (!blockParentElement) { return NS_ERROR_NULL_POINTER;}
+  if (NS_SUCCEEDED(res))
+  {
+    nsCOMPtr newParentNode;
+    nsCOMPtr blockParentNode = do_QueryInterface(blockParentElement);
+    // we need to treat nodes directly inside the body differently
+    nsAutoString parentTag;
+    blockParentElement->GetTagName(parentTag);
+    PRBool isRoot;
+    IsRootTag(parentTag, isRoot);
+    if (PR_TRUE==isRoot)    
+    {
+      // if nodeToReParent is a text node, we have Text.
+      // re-parent Text into a new  at the offset of Text in 
+      // so we end up with Text
+      // ignore aTransformation, replaces act like inserts
+      nsCOMPtr nodeAsText = do_QueryInterface(nodeToReParent);
+      if (nodeAsText)
+      {
+        res = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, 
+                                      aTransformation, getter_AddRefs(newParentNode));
+      }
+      else
+      { // this is the case of an insertion point between 2 non-text objects
+        // XXX: how to you know it's an insertion point???
+        PRInt32 offsetInParent=0;
+        res = GetChildOffset(nodeToReParent, blockParentNode, offsetInParent);
+        NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset");
+        // otherwise, just create the block parent at the selection
+        res = CreateNode(aParentTag, blockParentNode, offsetInParent, 
+                                          getter_AddRefs(newParentNode));
+        // XXX: need to move some of the children of blockParentNode into the newParentNode?
+        // XXX: need to create a bogus text node inside this new block?
+        //      that means, I need to generalize bogus node handling
+      }
+    }
+    else
+    { // the block parent is not a ROOT, 
+      // for the selected block content, transform blockParentNode 
+      if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) ||
+           (eInsertParent==aTransformation))
+      {
+		    if (gNoisy) { DebugDumpContent(); } // DEBUG
+        res = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, 
+                                      aTransformation, getter_AddRefs(newParentNode));
+        if ((NS_SUCCEEDED(res)) && (newParentNode) && (eReplaceParent==aTransformation))
+        {
+          PRBool hasChildren;
+          blockParentNode->HasChildNodes(&hasChildren);
+          if (PR_FALSE==hasChildren)
+          {
+            res = nsEditor::DeleteNode(blockParentNode);
+            if (gNoisy) 
+            {
+              printf("deleted old block parent node %p\n", blockParentNode.get());
+              DebugDumpContent(); // DEBUG
+            }
+          }
+        }
+      }
+      else { // otherwise, it's a no-op
+        if (gNoisy) { printf("AddBlockParent is a no-op for this collapsed selection.\n"); }
+      }
+    }
+  }
+  return res;
+}
+
+
+NS_IMETHODIMP 
+nsHTMLEditor::ReParentBlockContent(nsIDOMNode  *aNode, 
+                                   nsString    &aParentTag,
+                                   nsIDOMNode  *aBlockParentNode,
+                                   nsString    &aBlockParentTag,
+                                   BlockTransformationType aTransformation,
+                                   nsIDOMNode **aNewParentNode)
+{
+  if (!aNode || !aBlockParentNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; }
+  nsCOMPtr blockParentNode = do_QueryInterface(aBlockParentNode);
+  PRBool removeBlockParent = PR_FALSE;
+  PRBool removeBreakBefore = PR_FALSE;
+  PRBool removeBreakAfter = PR_FALSE;
+  nsCOMPtrancestor;
+  nsresult res = aNode->GetParentNode(getter_AddRefs(ancestor));
+  nsCOMPtrpreviousAncestor = do_QueryInterface(aNode);
+  while (NS_SUCCEEDED(res) && ancestor)
+  {
+    nsCOMPtrancestorElement = do_QueryInterface(ancestor);
+    nsAutoString ancestorTag;
+    ancestorElement->GetTagName(ancestorTag);
+    if (ancestorTag.EqualsIgnoreCase(aBlockParentTag))
+    {
+      break;  // previousAncestor will contain the node to operate on
+    }
+    previousAncestor = do_QueryInterface(ancestor);
+    res = ancestorElement->GetParentNode(getter_AddRefs(ancestor));
+  }
+  // now, previousAncestor is the node we are operating on
+  nsCOMPtrleftNode, rightNode;
+  res = GetBlockSection(previousAncestor, 
+                           getter_AddRefs(leftNode), 
+                           getter_AddRefs(rightNode));
+  if ((NS_SUCCEEDED(res)) && leftNode && rightNode)
+  {
+    // determine some state for managing 
s around the new block + PRBool isSubordinateBlock = PR_FALSE; // if true, the content is already in a subordinate block + PRBool isRootBlock = PR_FALSE; // if true, the content is in a root block + nsCOMPtrblockParentElement = do_QueryInterface(blockParentNode); + if (blockParentElement) + { + nsAutoString blockParentTag; + blockParentElement->GetTagName(blockParentTag); + IsSubordinateBlock(blockParentTag, isSubordinateBlock); + IsRootTag(blockParentTag, isRootBlock); + } + + if (PR_TRUE==isRootBlock) + { // we're creating a block element where a block element did not previously exist + removeBreakBefore = PR_TRUE; + removeBreakAfter = PR_TRUE; + } + + // apply the transformation + PRInt32 offsetInParent=0; + if (eInsertParent==aTransformation || PR_TRUE==isRootBlock) + { + res = GetChildOffset(leftNode, blockParentNode, offsetInParent); + NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); + res = CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); + if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } + } + else + { + nsCOMPtr grandParent; + res = blockParentNode->GetParentNode(getter_AddRefs(grandParent)); + if ((NS_SUCCEEDED(res)) && grandParent) + { + nsCOMPtrfirstChildNode, lastChildNode; + blockParentNode->GetFirstChild(getter_AddRefs(firstChildNode)); + blockParentNode->GetLastChild(getter_AddRefs(lastChildNode)); + if (firstChildNode==leftNode && lastChildNode==rightNode) + { + res = GetChildOffset(blockParentNode, grandParent, offsetInParent); + NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); + res = CreateNode(aParentTag, grandParent, offsetInParent, aNewParentNode); + if (gNoisy) { printf("created a node in grandParent at offset %d\n", offsetInParent); } + } + else + { + // We're in the case where the content of blockParentNode is separated by
's, + // creating multiple block content ranges. + // Split blockParentNode around the blockContent + if (gNoisy) { printf("splitting a node because of
s\n"); } + nsCOMPtr newLeftNode; + if (firstChildNode!=leftNode) + { + res = GetChildOffset(leftNode, blockParentNode, offsetInParent); + if (gNoisy) { printf("splitting left at %d\n", offsetInParent); } + res = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); + // after this split, blockParentNode still contains leftNode and rightNode + } + if (lastChildNode!=rightNode) + { + res = GetChildOffset(rightNode, blockParentNode, offsetInParent); + offsetInParent++; + if (gNoisy) { printf("splitting right at %d\n", offsetInParent); } + res = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); + blockParentNode = do_QueryInterface(newLeftNode); + } + res = GetChildOffset(leftNode, blockParentNode, offsetInParent); + NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); + res = CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); + if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } + // what we need to do here is remove the existing block parent when we're all done. + removeBlockParent = PR_TRUE; + } + } + } + if ((NS_SUCCEEDED(res)) && *aNewParentNode) + { // move all the children/contents of blockParentNode to aNewParentNode + nsCOMPtrchildNode = do_QueryInterface(rightNode); + nsCOMPtrpreviousSiblingNode; + while (NS_SUCCEEDED(res) && childNode) + { + childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); + // explicitly delete of childNode from it's current parent + // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! + res = nsEditor::DeleteNode(childNode); + if (NS_SUCCEEDED(res)) + { + res = nsEditor::InsertNode(childNode, *aNewParentNode, 0); + if (gNoisy) + { + printf("re-parented sibling node %p\n", childNode.get()); + } + } + if (childNode==leftNode || rightNode==leftNode) { + break; + } + childNode = do_QueryInterface(previousSiblingNode); + } // end while loop + } + // clean up the surrounding content to maintain vertical whitespace + if (NS_SUCCEEDED(res)) + { + // if the prior node is a
and we did something to change vertical whitespacing, delete the
+ nsCOMPtr brNode; + res = GetPriorNode(leftNode, PR_TRUE, getter_AddRefs(brNode)); + if (NS_SUCCEEDED(res) && brNode) + { + nsCOMPtr brContent = do_QueryInterface(brNode); + if (brContent) + { + nsCOMPtr brContentTag; + brContent->GetTag(*getter_AddRefs(brContentTag)); + if (nsIEditProperty::br==brContentTag.get()) { + res = DeleteNode(brNode); + } + } + } + + // if the next node is a
and we did something to change vertical whitespacing, delete the
+ if (NS_SUCCEEDED(res)) + { + res = GetNextNode(rightNode, PR_TRUE, getter_AddRefs(brNode)); + if (NS_SUCCEEDED(res) && brNode) + { + nsCOMPtr brContent = do_QueryInterface(brNode); + if (brContent) + { + nsCOMPtr brContentTag; + brContent->GetTag(*getter_AddRefs(brContentTag)); + if (nsIEditProperty::br==brContentTag.get()) { + res = DeleteNode(brNode); + } + } + } + } + } + if ((NS_SUCCEEDED(res)) && (PR_TRUE==removeBlockParent)) + { // we determined we need to remove the previous block parent. Do it! + // go through list backwards so deletes don't interfere with the iteration + nsCOMPtr childNodes; + res = blockParentNode->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(res)) && (childNodes)) + { + nsCOMPtrgrandParent; + blockParentNode->GetParentNode(getter_AddRefs(grandParent)); + //PRInt32 offsetInParent; + res = GetChildOffset(blockParentNode, grandParent, offsetInParent); + PRUint32 childCount; + childNodes->GetLength(&childCount); + PRInt32 i=childCount-1; + for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) + { + nsCOMPtr childNode; + res = childNodes->Item(i, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = DeleteNode(childNode); + if (NS_SUCCEEDED(res)) { + res = InsertNode(childNode, grandParent, offsetInParent); + } + } + } + if (gNoisy) { printf("removing the old block parent %p\n", blockParentNode.get()); } + res = DeleteNode(blockParentNode); + } + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, + nsString &aParentTag, + BlockTransformationType aTranformation) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsISupportsArray *blockSections; + res = NS_NewISupportsArray(&blockSections); + if ((NS_SUCCEEDED(res)) && blockSections) + { + res = GetBlockSectionsForRange(aRange, blockSections); + if (NS_SUCCEEDED(res)) + { + nsIDOMRange *subRange; + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + while (subRange && (NS_SUCCEEDED(res))) + { + nsCOMPtrstartParent; + res = subRange->GetStartParent(getter_AddRefs(startParent)); + if (NS_SUCCEEDED(res) && startParent) + { + if (gNoisy) { printf("ReParentContentOfRange calling ReParentContentOfNode\n"); } + res = ReParentContentOfNode(startParent, aParentTag, aTranformation); + } + NS_RELEASE(subRange); + blockSections->RemoveElementAt(0); + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + } + } + NS_RELEASE(blockSections); + } + return res; +} + + +NS_IMETHODIMP +nsHTMLEditor::RemoveParagraphStyleFromRange(nsIDOMRange *aRange) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsISupportsArray *blockSections; + res = NS_NewISupportsArray(&blockSections); + if ((NS_SUCCEEDED(res)) && blockSections) + { + res = GetBlockSectionsForRange(aRange, blockSections); + if (NS_SUCCEEDED(res)) + { + nsIDOMRange *subRange; + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + while (subRange && (NS_SUCCEEDED(res))) + { + res = RemoveParagraphStyleFromBlockContent(subRange); + NS_RELEASE(subRange); + blockSections->RemoveElementAt(0); + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + } + } + NS_RELEASE(blockSections); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsCOMPtrstartParent; + aRange->GetStartParent(getter_AddRefs(startParent)); + nsCOMPtrblockParentElement; + res = GetBlockParent(startParent, getter_AddRefs(blockParentElement)); + while ((NS_SUCCEEDED(res)) && blockParentElement) + { + nsAutoString blockParentTag; + blockParentElement->GetTagName(blockParentTag); + PRBool isSubordinateBlock; + IsSubordinateBlock(blockParentTag, isSubordinateBlock); + if (PR_FALSE==isSubordinateBlock) { + break; + } + else + { + // go through list backwards so deletes don't interfere with the iteration + nsCOMPtr childNodes; + res = blockParentElement->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(res)) && (childNodes)) + { + nsCOMPtrgrandParent; + blockParentElement->GetParentNode(getter_AddRefs(grandParent)); + PRInt32 offsetInParent; + res = GetChildOffset(blockParentElement, grandParent, offsetInParent); + PRUint32 childCount; + childNodes->GetLength(&childCount); + PRInt32 i=childCount-1; + for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) + { + nsCOMPtr childNode; + res = childNodes->Item(i, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = DeleteNode(childNode); + if (NS_SUCCEEDED(res)) { + res = InsertNode(childNode, grandParent, offsetInParent); + } + } + } + if (NS_SUCCEEDED(res)) { + res = DeleteNode(blockParentElement); + } + } + } + res = GetBlockParent(startParent, getter_AddRefs(blockParentElement)); + } + return res; +} + + +NS_IMETHODIMP +nsHTMLEditor::RemoveParentFromRange(const nsString &aParentTag, nsIDOMRange *aRange) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsISupportsArray *blockSections; + res = NS_NewISupportsArray(&blockSections); + if ((NS_SUCCEEDED(res)) && blockSections) + { + res = GetBlockSectionsForRange(aRange, blockSections); + if (NS_SUCCEEDED(res)) + { + nsIDOMRange *subRange; + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + while (subRange && (NS_SUCCEEDED(res))) + { + res = RemoveParentFromBlockContent(aParentTag, subRange); + NS_RELEASE(subRange); + blockSections->RemoveElementAt(0); + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + } + } + NS_RELEASE(blockSections); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRange *aRange) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsCOMPtrstartParent; + res = aRange->GetStartParent(getter_AddRefs(startParent)); + if ((NS_SUCCEEDED(res)) && startParent) + { + nsCOMPtrparentNode; + nsCOMPtrparentElement; + res = startParent->GetParentNode(getter_AddRefs(parentNode)); + while ((NS_SUCCEEDED(res)) && parentNode) + { + parentElement = do_QueryInterface(parentNode); + nsAutoString parentTag; + parentElement->GetTagName(parentTag); + PRBool isRoot; + IsRootTag(parentTag, isRoot); + if (aParentTag.EqualsIgnoreCase(parentTag)) + { + // go through list backwards so deletes don't interfere with the iteration + nsCOMPtr childNodes; + res = parentElement->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(res)) && (childNodes)) + { + nsCOMPtrgrandParent; + parentElement->GetParentNode(getter_AddRefs(grandParent)); + PRInt32 offsetInParent; + res = GetChildOffset(parentElement, grandParent, offsetInParent); + PRUint32 childCount; + childNodes->GetLength(&childCount); + PRInt32 i=childCount-1; + for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) + { + nsCOMPtr childNode; + res = childNodes->Item(i, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = DeleteNode(childNode); + if (NS_SUCCEEDED(res)) { + res = InsertNode(childNode, grandParent, offsetInParent); + } + } + } + if (NS_SUCCEEDED(res)) { + res = DeleteNode(parentElement); + } + } + break; + } + else if (PR_TRUE==isRoot) { // hit a local root node, terminate loop + break; + } + res = parentElement->GetParentNode(getter_AddRefs(parentNode)); + } + } + return res; +} + + + +PRBool nsHTMLEditor::IsElementInBody(nsIDOMElement* aElement) +{ + if ( aElement ) + { + nsIDOMElement* bodyElement = nsnull; + nsresult res = nsEditor::GetBodyElement(&bodyElement); + if (NS_SUCCEEDED(res) && bodyElement) + { + nsCOMPtr parent; + nsCOMPtr currentElement = do_QueryInterface(aElement); + if (currentElement) + { + do { + currentElement->GetParentNode(getter_AddRefs(parent)); + if (parent) + { + if (parent == bodyElement) + return PR_TRUE; + + currentElement = parent; + } + } while(parent); + } + } + } + return PR_FALSE; +} + +PRBool +nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement) +{ + PRBool caretIsSet = PR_FALSE; + + if (aElement && IsElementInBody(aElement)) + { + nsresult res = NS_OK; + nsAutoString tagName; + aElement->GetNodeName(tagName); + tagName.ToLowerCase(); + if (tagName == "table" || tagName == "tr" || + tagName == "td" || tagName == "th" || + tagName == "thead" || tagName == "tfoot" || + tagName == "tbody" || tagName == "caption") + { + nsCOMPtr node = do_QueryInterface(aElement); + nsCOMPtr parent; + // This MUST succeed if IsElementInBody was TRUE + node->GetParentNode(getter_AddRefs(parent)); + nsCOMPtrfirstChild; + // Find deepest child + PRBool hasChild; + while (NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild) + { + if (NS_SUCCEEDED(node->GetFirstChild(getter_AddRefs(firstChild)))) + { + parent = node; + node = firstChild; + } + } + PRInt32 offset = 0; + nsCOMPtrlastChild; + res = parent->GetLastChild(getter_AddRefs(lastChild)); + if (NS_SUCCEEDED(res) && lastChild && node != lastChild) + { + if (node == lastChild) + { + // Check if node is text and has more than just a   + nsCOMPtrtextNode = do_QueryInterface(node); + nsString text; + char nbspStr[2] = {nbsp, 0}; + if (textNode && textNode->GetData(text)) + { + // Set selection relative to the text node + parent = node; + PRInt32 len = text.Length(); + if (len > 1 || text != nbspStr) + { + offset = len; + } + } + } else { + // We have > 1 node, so set to end of content + } + } + // Set selection at beginning of deepest node + // Should we set + nsCOMPtr selection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(res) && selection) + { + res = selection->Collapse(parent, offset); + if (NS_SUCCEEDED(res)) + caretIsSet = PR_TRUE; + } + } + } + return caretIsSet; +} + + + NS_IMETHODIMP nsHTMLEditor::IsRootTag(nsString &aTag, PRBool &aIsTag) { @@ -2758,54 +4341,1342 @@ nsHTMLEditor::IsSubordinateBlock(nsString &aTag, PRBool &aIsTag) return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::BeginComposition(void) -{ - return nsTextEditor::BeginComposition(); -} - -NS_IMETHODIMP nsHTMLEditor::EndComposition(void) -{ - return nsTextEditor::EndComposition(); -} - -NS_IMETHODIMP nsHTMLEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList, nsTextEventReply* aReply) -{ - return nsTextEditor::SetCompositionString(aCompositionString,aTextRangeList,aReply); -} NS_IMETHODIMP -nsHTMLEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) +nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode) { -#ifdef DEBUG - if (!outNumTests || !outNumTestsFailed) - return NS_ERROR_NULL_POINTER; - - // first, run the text editor tests (is this appropriate?) - nsresult rv = nsTextEditor::DebugUnitTests(outNumTests, outNumTestsFailed); - if (NS_FAILED(rv)) - return rv; - - // now run our tests - - - - *outNumTests += 0; - *outNumTestsFailed += 0; - return NS_OK; -#else - return NS_ERROR_NOT_IMPLEMENTED; + nsresult result=NS_ERROR_NOT_INITIALIZED; + nsCOMPtr selection; + result = GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + PRBool collapsed; + result = selection->GetIsCollapsed(&collapsed); + if (NS_SUCCEEDED(result) && !collapsed) + { + result = DeleteSelection(nsIEditor::eDoNothing); + if (NS_FAILED(result)) { + return result; + } + // get the new selection + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) { + return result; + } +#ifdef NS_DEBUG + nsCOMPtrtestSelectedNode; + nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode)); + // no selection is ok. + // if there is a selection, it must be collapsed + if (testSelectedNode) + { + PRBool testCollapsed; + debugResult = selection->GetIsCollapsed(&testCollapsed); + NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); + NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion"); + } #endif + } + // split the selected node + PRInt32 offsetOfSelectedNode; + result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); + if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode) + { + nsCOMPtr selectedNode; + PRUint32 selectedNodeContentCount=0; + nsCOMPtrselectedParentNodeAsText; + selectedParentNodeAsText = do_QueryInterface(parentSelectedNode); + + /* if the selection is a text node, split the text node if necesary + and compute where to put the new node + */ + if (selectedParentNodeAsText) + { + PRInt32 indexOfTextNodeInParent; + selectedNode = do_QueryInterface(parentSelectedNode); + selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode)); + selectedParentNodeAsText->GetLength(&selectedNodeContentCount); + GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent); + + if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) + { + nsCOMPtr newSiblingNode; + result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); + // now get the node's offset in it's parent, and insert the new tag there + if (NS_SUCCEEDED(result)) { + result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + } + } + else + { // determine where to insert the new node + if (0==offsetOfSelectedNode) { + offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent + } + else { // insert new node as last child + GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node + } + } + } + /* if the selection is not a text node, split the parent node if necesary + and compute where to put the new node + */ + else + { // it's an interior node + nsCOMPtrparentChildList; + parentSelectedNode->GetChildNodes(getter_AddRefs(parentChildList)); + if ((NS_SUCCEEDED(result)) && parentChildList) + { + result = parentChildList->Item(offsetOfSelectedNode, getter_AddRefs(selectedNode)); + if ((NS_SUCCEEDED(result)) && selectedNode) + { + nsCOMPtrselectedNodeAsText; + selectedNodeAsText = do_QueryInterface(selectedNode); + nsCOMPtrchildList; + //CM: I added "result =" + result = selectedNode->GetChildNodes(getter_AddRefs(childList)); + if (NS_SUCCEEDED(result)) + { + if (childList) + { + childList->GetLength(&selectedNodeContentCount); + } + else + { + // This is the case where the collapsed selection offset + // points to an inline node with no children + // This must also be where the new node should be inserted + // and there is no splitting necessary + offsetOfNewNode = offsetOfSelectedNode; + return NS_OK; + } + } + else + { + return NS_ERROR_FAILURE; + } + if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) + { + nsCOMPtr newSiblingNode; + result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); + // now get the node's offset in it's parent, and insert the new tag there + if (NS_SUCCEEDED(result)) { + result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + } + } + else + { // determine where to insert the new node + if (0==offsetOfSelectedNode) { + offsetOfNewNode = 0; // insert new node as first child + } + else { // insert new node as last child + GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node + } + } + } + } + } + + // Here's where the new node was inserted + } + else { + printf("InsertBreak into an empty document is not yet supported\n"); + } + } + return result; +} + + +#pragma mark - + +nsCOMPtr nsHTMLEditor::FindPreElement() +{ + nsCOMPtr domdoc; + nsEditor::GetDocument(getter_AddRefs(domdoc)); + if (!domdoc) + return 0; + + nsCOMPtr doc (do_QueryInterface(domdoc)); + if (!doc) + return 0; + + nsIContent* rootContent = doc->GetRootContent(); + if (!rootContent) + return 0; + + nsCOMPtr rootNode (do_QueryInterface(rootContent)); + if (!rootNode) + return 0; + + nsString prestr ("PRE"); // GetFirstNodeOfType requires capitals + nsCOMPtr preNode; + if (!NS_SUCCEEDED(nsEditor::GetFirstNodeOfType(rootNode, prestr, + getter_AddRefs(preNode)))) + return 0; + + return do_QueryInterface(preNode); +} + + +void nsHTMLEditor::HandleEventListenerError() +{ + printf("failed to add event listener\n"); + // null out the nsCOMPtrs + mKeyListenerP = nsnull; + mMouseListenerP = nsnull; + mTextListenerP = nsnull; + mDragListenerP = nsnull; + mCompositionListenerP = nsnull; + mFocusListenerP = nsnull; +} + + +TypeInState * nsHTMLEditor::GetTypeInState() +{ + if (mTypeInState) { + NS_ADDREF(mTypeInState); + } + return mTypeInState; +} + + +NS_IMETHODIMP +nsHTMLEditor::SetTextPropertiesForNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue) +{ + if (gNoisy) { printf("nsTextEditor::SetTextPropertyForNode\n"); } + nsresult result=NS_OK; + PRBool textPropertySet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(aNode, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); + if (PR_FALSE==textPropertySet) + { + if (aValue && 0!=aValue->Length()) + { + result = RemoveTextPropertiesForNode(aNode, aParent, aStartOffset, aEndOffset, aPropName, aAttribute); + } + nsAutoString tag; + aPropName->ToString(tag); + if (NS_SUCCEEDED(result)) + { + nsCOMPtrnewStyleNode; + result = nsEditor::CreateNode(tag, aParent, 0, getter_AddRefs(newStyleNode)); + if (NS_SUCCEEDED(result) && newStyleNode) + { + nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(aNode); + if (nodeAsChar) + { + result = MoveContentOfNodeIntoNewParent(aNode, newStyleNode, aStartOffset, aEndOffset); + } + else + { // handle non-text selection + nsCOMPtr parent; // used just to make the code easier to understand + nsCOMPtr child; + parent = do_QueryInterface(aNode); + child = GetChildAt(parent, aStartOffset); + // XXX: need to loop for aStartOffset!=aEndOffset-1? + PRInt32 offsetInParent = aStartOffset; // remember where aNode was in aParent + if (NS_SUCCEEDED(result)) + { // remove child from parent + result = nsEditor::DeleteNode(child); + if (NS_SUCCEEDED(result)) + { // put child into the newStyleNode + result = nsEditor::InsertNode(child, newStyleNode, 0); + if (NS_SUCCEEDED(result)) + { // put newStyleNode in parent where child was + result = nsEditor::InsertNode(newStyleNode, parent, offsetInParent); + } + } + } + } + if (NS_SUCCEEDED(result)) + { + if (aAttribute && 0!=aAttribute->Length()) + { + nsCOMPtr newStyleElement; + newStyleElement = do_QueryInterface(newStyleNode); + nsAutoString value; + if (aValue) { + value = *aValue; + } + // XXX should be a call to editor to change attribute! + result = newStyleElement->SetAttribute(*aAttribute, value); + } + } + } + } + } + if (gNoisy) { printf("SetTextPropertyForNode returning %d, dumping content...\n", result);} + if (gNoisy) {DebugDumpContent(); } // DEBUG + return result; +} + +NS_IMETHODIMP nsHTMLEditor::MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, + nsIDOMNode *aNewParentNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset) +{ + if (!aNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; } + if (gNoisy) { printf("nsTextEditor::MoveContentOfNodeIntoNewParent\n"); } + nsresult result=NS_OK; + + PRUint32 count; + result = GetLengthOfDOMNode(aNode, count); + + if (NS_SUCCEEDED(result)) + { + nsCOMPtrnewChildNode; // this will be the child node we move into the new style node + // split the node at the start offset unless the split would create an empty node + if (aStartOffset!=0) + { + result = nsEditor::SplitNode(aNode, aStartOffset, getter_AddRefs(newChildNode)); + if (gNoisy) { printf("* split created left node %p\n", newChildNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + if (NS_SUCCEEDED(result)) + { + if (aEndOffset!=(PRInt32)count) + { + result = nsEditor::SplitNode(aNode, aEndOffset-aStartOffset, getter_AddRefs(newChildNode)); + if (gNoisy) { printf("* split created left node %p\n", newChildNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + else + { + newChildNode = do_QueryInterface(aNode); + if (gNoisy) { printf("* second split not required, new text node set to aNode = %p\n", newChildNode.get());} + } + if (NS_SUCCEEDED(result)) + { + // move aNewParentNode into the right location + + // optimization: if all we're doing is changing a value for an existing attribute for the + // entire selection, then just twiddle the existing style node + PRBool done = PR_FALSE; // set to true in optimized case if we can really do the optimization + /* + if (aAttribute && aValue && (0==aStartOffset) && (aEndOffset==(PRInt32)count)) + { + // ??? can we really compute this? + } + */ + if (PR_FALSE==done) + { + // if we've ended up with an empty text node, just delete it and we're done + nsCOMPtrnewChildNodeAsChar; + newChildNodeAsChar = do_QueryInterface(newChildNode); + PRUint32 newChildNodeLength; + if (newChildNodeAsChar) + { + newChildNodeAsChar->GetLength(&newChildNodeLength); + if (0==newChildNodeLength) + { + result = nsEditor::DeleteNode(newChildNode); + done = PR_TRUE; + // XXX: need to set selection here + } + } + // move the new child node into the new parent + if (PR_FALSE==done) + { + // first, move the new parent into the correct location + PRInt32 offsetInParent; + nsCOMPtrparentNode; + result = aNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::DeleteNode(aNewParentNode); + if (NS_SUCCEEDED(result)) + { // must get child offset AFTER delete of aNewParentNode! + result = GetChildOffset(aNode, parentNode, offsetInParent); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::InsertNode(aNewParentNode, parentNode, offsetInParent); + if (NS_SUCCEEDED(result)) + { + // then move the new child into the new parent node + result = nsEditor::DeleteNode(newChildNode); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::InsertNode(newChildNode, aNewParentNode, 0); + if (NS_SUCCEEDED(result)) + { // set the selection + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result)) + { + selection->Collapse(newChildNode, 0); + PRInt32 endOffset = aEndOffset-aStartOffset; + selection->Extend(newChildNode, endOffset); + } + } + } + } + } + } + } + } + } + } + } + } + return result; +} + +/* this should only get called if the only intervening nodes are inline style nodes */ +NS_IMETHODIMP +nsHTMLEditor::SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue) +{ + if (gNoisy) { printf("nsTextEditor::SetTextPropertiesForNodesWithSameParent\n"); } + nsresult result=NS_OK; + PRBool textPropertySet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(aStartNode, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); + if (PR_FALSE==textPropertySet) + { + if (aValue && 0!=aValue->Length()) + { + result = RemoveTextPropertiesForNodesWithSameParent(aStartNode, aStartOffset, + aEndNode, aEndOffset, + aParent, aPropName, aAttribute); + } + nsAutoString tag; + aPropName->ToString(tag); + // create the new style node, which will be the new parent for the selected nodes + nsCOMPtrnewStyleNode; + nsCOMPtrdoc; + result = GetDocument(getter_AddRefs(doc)); + if (NS_SUCCEEDED(result) && doc) + { + nsCOMPtrnewElement; + result = doc->CreateElement(tag, getter_AddRefs(newElement)); + if (NS_SUCCEEDED(result) && newElement) + { + newStyleNode = do_QueryInterface(newElement); + } + } + if (NS_SUCCEEDED(result) && newStyleNode) + { + result = MoveContiguousContentIntoNewParent(aStartNode, aStartOffset, aEndNode, aEndOffset, aParent, newStyleNode); + if (NS_SUCCEEDED(result) && aAttribute && 0!=aAttribute->Length()) + { + nsCOMPtr newStyleElement; + newStyleElement = do_QueryInterface(newStyleNode); + nsAutoString value; + if (aValue) { + value = *aValue; + } + result = newStyleElement->SetAttribute(*aAttribute, value); + } + } + } + return result; +} + +//XXX won't work for selections that are not leaf nodes! +// should fix up the end points to make sure they are leaf nodes +NS_IMETHODIMP +nsHTMLEditor::MoveContiguousContentIntoNewParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aGrandParentNode, + nsIDOMNode *aNewParentNode) +{ + if (!aStartNode || !aEndNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; } + if (gNoisy) { printf("nsTextEditor::MoveContiguousContentIntoNewParent\n"); } + + nsresult result = NS_OK; + nsCOMPtrstartNode, endNode; + PRInt32 startOffset = aStartOffset; // this will be the left edge of what we change + PRInt32 endOffset = aEndOffset; // this will be the right edge of what we change + nsCOMPtrnewLeftNode; // this will be the middle text node + if (IsTextNode(aStartNode)) + { + startOffset = 0; + if (gNoisy) { printf("aStartNode is a text node.\n"); } + startNode = do_QueryInterface(aStartNode); + if (0!=aStartOffset) + { + result = nsEditor::SplitNode(aStartNode, aStartOffset, getter_AddRefs(newLeftNode)); + if (gNoisy) { printf("split aStartNode, newLeftNode = %p\n", newLeftNode.get()); } + } + else { + if (gNoisy) { printf("did not split aStartNode\n"); } + } + } + else { + startNode = GetChildAt(aStartNode, aStartOffset); + if (gNoisy) { printf("aStartNode is not a text node, got startNode = %p.\n", startNode.get()); } + } + if (NS_SUCCEEDED(result)) + { + nsCOMPtrnewRightNode; // this will be the middle text node + if (IsTextNode(aEndNode)) + { + if (gNoisy) { printf("aEndNode is a text node.\n"); } + endNode = do_QueryInterface(aEndNode); + PRUint32 count; + GetLengthOfDOMNode(aEndNode, count); + if ((PRInt32)count!=aEndOffset) + { + result = nsEditor::SplitNode(aEndNode, aEndOffset, getter_AddRefs(newRightNode)); + if (gNoisy) { printf("split aEndNode, newRightNode = %p\n", newRightNode.get()); } + } + else { + newRightNode = do_QueryInterface(aEndNode); + if (gNoisy) { printf("did not split aEndNode\n"); } + } + } + else + { + endNode = GetChildAt(aEndNode, aEndOffset-1); + newRightNode = do_QueryInterface(endNode); + if (gNoisy) { printf("aEndNode is not a text node, got endNode = %p.\n", endNode.get()); } + } + if (NS_SUCCEEDED(result)) + { + PRInt32 offsetInParent; + result = GetChildOffset(startNode, aGrandParentNode, offsetInParent); + /* + if (newLeftNode) { + result = GetChildOffset(newLeftNode, aGrandParentNode, offsetInParent); + } + else { + offsetInParent = 0; // relies on +1 below in call to CreateNode + } + */ + if (NS_SUCCEEDED(result)) + { + // insert aNewParentNode into aGrandParentNode + result = nsEditor::InsertNode(aNewParentNode, aGrandParentNode, offsetInParent); + if (NS_SUCCEEDED(result)) + { // move the right half of the start node into the new parent node + nsCOMPtrintermediateNode; + result = startNode->GetNextSibling(getter_AddRefs(intermediateNode)); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::DeleteNode(startNode); + if (NS_SUCCEEDED(result)) + { + PRInt32 childIndex=0; + result = nsEditor::InsertNode(startNode, aNewParentNode, childIndex); + childIndex++; + if (NS_SUCCEEDED(result)) + { // move all the intermediate nodes into the new parent node + nsCOMPtrnextSibling; + while (intermediateNode.get() != endNode.get()) + { + if (!intermediateNode) + result = NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) { + break; + } + // get the next sibling before moving the current child!!! + intermediateNode->GetNextSibling(getter_AddRefs(nextSibling)); + result = nsEditor::DeleteNode(intermediateNode); + if (NS_SUCCEEDED(result)) { + result = nsEditor::InsertNode(intermediateNode, aNewParentNode, childIndex); + childIndex++; + } + intermediateNode = do_QueryInterface(nextSibling); + } + if (NS_SUCCEEDED(result)) + { // move the left half of the end node into the new parent node + result = nsEditor::DeleteNode(newRightNode); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::InsertNode(newRightNode, aNewParentNode, childIndex); + // now set the selection + if (NS_SUCCEEDED(result)) + { + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result)) + { + selection->Collapse(startNode, startOffset); + selection->Extend(newRightNode, endOffset); + } + } + } + } + } + } + } + } + } + } + } + return result; +} + + +/* this wraps every selected text node in a new inline style node if needed + the text nodes are treated as being unique -- each needs it's own style node + if the style is not already present. + each action has immediate effect on the content tree and resolved style, so + doing outermost text nodes first removes the need for interior style nodes in some cases. + XXX: need to code test to see if new style node is needed +*/ +NS_IMETHODIMP +nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, + nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue) +{ + if (gNoisy) { printf("start nsTextEditor::SetTextPropertiesForNodeWithDifferentParents\n"); } + nsresult result=NS_OK; + PRUint32 count; + if (!aRange || !aStartNode || !aEndNode || !aParent || !aPropName) + return NS_ERROR_NULL_POINTER; + + PRInt32 startOffset, endOffset; + // create a style node for the text in the start parent + nsCOMPtrparent; + + result = RemoveTextPropertiesForNodeWithDifferentParents(aStartNode,aStartOffset, + aEndNode, aEndOffset, + aParent, aPropName, aAttribute); + if (NS_FAILED(result)) { return result; } + + // RemoveTextProperties... might have changed selection endpoints, get new ones + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) { return result; } + if (!selection) { return NS_ERROR_NULL_POINTER; } + selection->GetAnchorOffset(&aStartOffset); + selection->GetFocusOffset(&aEndOffset); + + // create new parent nodes for all the content between the start and end nodes + nsCOMPtriter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + // find our starting point + PRBool startIsText = IsTextNode(aStartNode); + nsCOMPtrstartContent; + if (PR_TRUE==startIsText) { + startContent = do_QueryInterface(aStartNode); + } + else { + nsCOMPtrnode = GetChildAt(aStartNode, aStartOffset); + startContent = do_QueryInterface(node); + } + + // find our ending point + PRBool endIsText = IsTextNode(aEndNode); + nsCOMPtrendContent; + if (PR_TRUE==endIsText) { + endContent = do_QueryInterface(aEndNode); + } + else + { + nsCOMPtrtheEndNode; + if (aEndOffset>0) + { + theEndNode = GetChildAt(aEndNode, aEndOffset-1); + } + else { + // XXX: we need to find the previous node and set the selection correctly + NS_ASSERTION(0, "unexpected selection"); + return NS_ERROR_NOT_IMPLEMENTED; + } + endContent = do_QueryInterface(theEndNode); + } + + if (!startContent || !endContent) { return NS_ERROR_NULL_POINTER; } + // iterate over the nodes between the starting and ending points + iter->Init(aRange); + nsCOMPtr content; + iter->CurrentNode(getter_AddRefs(content)); + nsAutoString tag; + aPropName->ToString(tag); + while (NS_COMFALSE == iter->IsDone()) + { + if ((content.get() != startContent.get()) && + (content.get() != endContent.get())) + { + nsCOMPtrnode; + node = do_QueryInterface(content); + if (IsEditable(node)) + { + PRBool canContainChildren; + content->CanContainChildren(canContainChildren); + if (PR_FALSE==canContainChildren) + { + nsEditor::GetTagString(node,tag); + if (tag != "br") // skip
, even though it's a leaf + { // only want to wrap the text node in a new style node if it doesn't already have that style + if (gNoisy) { printf("node %p is an editable leaf.\n", node.get()); } + PRBool textPropertySet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(node, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); + if (PR_FALSE==textPropertySet) + { + if (gNoisy) { printf("property not set\n"); } + node->GetParentNode(getter_AddRefs(parent)); + if (!parent) { return NS_ERROR_NULL_POINTER; } + nsCOMPtrparentContent; + parentContent = do_QueryInterface(parent); + nsCOMPtrparentNode = do_QueryInterface(parent); + if (PR_TRUE==IsTextNode(node)) + { + startOffset = 0; + result = GetLengthOfDOMNode(node, (PRUint32&)endOffset); + } + else + { + parentContent->IndexOf(content, startOffset); + endOffset = startOffset+1; + } + if (gNoisy) { printf("start/end = %d %d\n", startOffset, endOffset); } + if (NS_SUCCEEDED(result)) { + result = SetTextPropertiesForNode(node, parentNode, startOffset, endOffset, aPropName, aAttribute, aValue); + } + } + } + } + } + // XXX: shouldn't there be an else here for non-text leaf nodes? + } + // note we don't check the result, we just rely on iter->IsDone + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + // handle endpoints + if (NS_SUCCEEDED(result)) + { + // create a style node for the text in the start parent + nsCOMPtrstartNode = do_QueryInterface(startContent); + result = startNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(result)) { + return result; + } + nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(startNode); + if (nodeAsChar) + { + nodeAsChar->GetLength(&count); + if (gNoisy) { printf("processing start node %p.\n", nodeAsChar.get()); } + result = SetTextPropertiesForNode(startNode, parent, aStartOffset, count, aPropName, aAttribute, aValue); + startOffset = 0; + } + else + { + nsCOMPtrgrandParent; + result = parent->GetParentNode(getter_AddRefs(grandParent)); + if (gNoisy) { printf("processing start node %p.\n", parent.get()); } + result = SetTextPropertiesForNode(parent, grandParent, aStartOffset, aStartOffset+1, aPropName, aAttribute, aValue); + startNode = do_QueryInterface(parent); + startOffset = aStartOffset; + } + + + if (NS_SUCCEEDED(result)) + { + // create a style node for the text in the end parent + nsCOMPtrendNode = do_QueryInterface(endContent); + result = endNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(result)) { + return result; + } + nodeAsChar = do_QueryInterface(endNode); + if (nodeAsChar) + { + nodeAsChar->GetLength(&count); + if (gNoisy) { printf("processing end node %p.\n", nodeAsChar.get()); } + result = SetTextPropertiesForNode(endNode, parent, 0, aEndOffset, aPropName, aAttribute, aValue); + // SetTextPropertiesForNode kindly computed the proper selection focus node and offset for us, + // remember them here + selection->GetFocusOffset(&endOffset); + selection->GetFocusNode(getter_AddRefs(endNode)); + } + else + { + NS_ASSERTION(0!=aEndOffset, "unexpected selection end offset"); + if (0==aEndOffset) { return NS_ERROR_NOT_IMPLEMENTED; } + nsCOMPtrgrandParent; + result = parent->GetParentNode(getter_AddRefs(grandParent)); + if (gNoisy) { printf("processing end node %p.\n", parent.get()); } + result = SetTextPropertiesForNode(parent, grandParent, aEndOffset-1, aEndOffset, aPropName, aAttribute, aValue); + endNode = do_QueryInterface(parent); + endOffset = 0; + } + if (NS_SUCCEEDED(result)) + { + + selection->Collapse(startNode, startOffset); + selection->Extend(endNode, aEndOffset); + } + } + } + } + if (gNoisy) { printf("end nsTextEditor::SetTextPropertiesForNodeWithDifferentParents\n"); } + + return result; } NS_IMETHODIMP -nsHTMLEditor::StartLogging(nsIFileSpec *aLogFile) +nsHTMLEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aPropName, + const nsString *aAttribute) { - return nsTextEditor::StartLogging(aLogFile); + if (gNoisy) { printf("nsTextEditor::RemoveTextPropertyForNode\n"); } + nsresult result=NS_OK; + nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(aNode); + PRBool textPropertySet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(aNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); + if (PR_TRUE==textPropertySet) + { + nsCOMPtrparent; // initially set to first interior parent node to process + nsCOMPtrnewMiddleNode; // this will be the middle node after any required splits + nsCOMPtrnewLeftNode; // this will be the leftmost node, + // the node being split will be rightmost + PRUint32 count; + // if aNode is a text node, treat is specially + if (nodeAsChar) + { + nodeAsChar->GetLength(&count); + // split the node, and all parent nodes up to the style node + // then promote the selected content to the parent of the style node + if (0!=aStartOffset) { + if (gNoisy) { printf("* splitting text node %p at %d\n", aNode, aStartOffset);} + result = nsEditor::SplitNode(aNode, aStartOffset, getter_AddRefs(newLeftNode)); + if (gNoisy) { printf("* split created left node %p\n", newLeftNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + if (NS_SUCCEEDED(result)) + { + if ((PRInt32)count!=aEndOffset) { + if (gNoisy) { printf("* splitting text node (right node) %p at %d\n", aNode, aEndOffset-aStartOffset);} + result = nsEditor::SplitNode(aNode, aEndOffset-aStartOffset, getter_AddRefs(newMiddleNode)); + if (gNoisy) { printf("* split created middle node %p\n", newMiddleNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + else { + if (gNoisy) { printf("* no need to split text node, middle to aNode\n");} + newMiddleNode = do_QueryInterface(aNode); + } + NS_ASSERTION(newMiddleNode, "no middle node created"); + // now that the text node is split, split parent nodes until we get to the style node + parent = do_QueryInterface(aParent); // we know this has to succeed, no need to check + } + } + else { + newMiddleNode = do_QueryInterface(aNode); + parent = do_QueryInterface(aParent); + } + if (NS_SUCCEEDED(result) && newMiddleNode) + { + // split every ancestor until we find the node that is giving us the style we want to remove + // then split the style node and promote the selected content to the style node's parent + while (NS_SUCCEEDED(result) && parent) + { + if (gNoisy) { printf("* looking at parent %p\n", parent.get());} + // get the tag from parent and see if we're done + nsCOMPtrtemp; + nsCOMPtrelement; + element = do_QueryInterface(parent); + if (element) + { + nsAutoString tag; + result = element->GetTagName(tag); + if (gNoisy) { printf("* parent has tag %s\n", tag.ToNewCString()); } // XXX leak! + if (NS_SUCCEEDED(result)) + { + if (PR_FALSE==tag.EqualsIgnoreCase(aPropName->GetUnicode())) + { + PRInt32 offsetInParent; + result = GetChildOffset(newMiddleNode, parent, offsetInParent); + if (NS_SUCCEEDED(result)) + { + if (0!=offsetInParent) { + if (gNoisy) { printf("* splitting parent %p at offset %d\n", parent.get(), offsetInParent);} + result = nsEditor::SplitNode(parent, offsetInParent, getter_AddRefs(newLeftNode)); + if (gNoisy) { printf("* split created left node %p sibling of parent\n", newLeftNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + if (NS_SUCCEEDED(result)) + { + nsCOMPtrchildNodes; + result = parent->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_SUCCEEDED(result) && childNodes) + { + childNodes->GetLength(&count); + NS_ASSERTION(count>0, "bad child count in newly split node"); + if ((PRInt32)count!=1) + { + if (gNoisy) { printf("* splitting parent %p at offset %d\n", parent.get(), 1);} + result = nsEditor::SplitNode(parent, 1, getter_AddRefs(newMiddleNode)); + if (gNoisy) { printf("* split created middle node %p sibling of parent\n", newMiddleNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + else { + if (gNoisy) { printf("* no need to split parent, newMiddleNode=parent\n");} + newMiddleNode = do_QueryInterface(parent); + } + NS_ASSERTION(newMiddleNode, "no middle node created"); + parent->GetParentNode(getter_AddRefs(temp)); + parent = do_QueryInterface(temp); + } + } + } + } + // else we've found the style tag (referred to by "parent") + // newMiddleNode is the node that is an ancestor to the selection + else + { + if (gNoisy) { printf("* this is the style node\n");} + PRInt32 offsetInParent; + result = GetChildOffset(newMiddleNode, parent, offsetInParent); + if (NS_SUCCEEDED(result)) + { + nsCOMPtrchildNodes; + result = parent->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_SUCCEEDED(result) && childNodes) + { + childNodes->GetLength(&count); + // if there are siblings to the right, split parent at offsetInParent+1 + if ((PRInt32)count!=offsetInParent+1) + { + nsCOMPtrnewRightNode; + //nsCOMPtrtemp; + if (gNoisy) { printf("* splitting parent %p at offset %d for right side\n", parent.get(), offsetInParent+1);} + result = nsEditor::SplitNode(parent, offsetInParent+1, getter_AddRefs(temp)); + if (NS_SUCCEEDED(result)) + { + newRightNode = do_QueryInterface(parent); + parent = do_QueryInterface(temp); + if (gNoisy) { printf("* split created right node %p sibling of parent %p\n", newRightNode.get(), parent.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + } + if (NS_SUCCEEDED(result) && 0!=offsetInParent) { + if (gNoisy) { printf("* splitting parent %p at offset %d for left side\n", parent.get(), offsetInParent);} + result = nsEditor::SplitNode(parent, offsetInParent, getter_AddRefs(newLeftNode)); + if (gNoisy) { printf("* split created left node %p sibling of parent %p\n", newLeftNode.get(), parent.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + if (NS_SUCCEEDED(result)) + { // promote the selection to the grandparent + // first, determine the child's position in it's parent + PRInt32 childPositionInParent; + GetChildOffset(newMiddleNode, parent, childPositionInParent); + // compare childPositionInParent to the number of children in parent + //PRUint32 count=0; + //nsCOMPtrchildNodes; + result = parent->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_SUCCEEDED(result) && childNodes) { + childNodes->GetLength(&count); + } + PRBool insertAfter = PR_FALSE; + // if they're equal, we'll insert newMiddleNode in grandParent after the parent + if ((PRInt32)count==childPositionInParent) { + insertAfter = PR_TRUE; + } + // now that we know where to put newMiddleNode, do it. + nsCOMPtrgrandParent; + result = parent->GetParentNode(getter_AddRefs(grandParent)); + if (NS_SUCCEEDED(result) && grandParent) + { + if (gNoisy) { printf("* deleting middle node %p\n", newMiddleNode.get());} + result = nsEditor::DeleteNode(newMiddleNode); + if (gNoisy) {DebugDumpContent(); } // DEBUG + if (NS_SUCCEEDED(result)) + { + PRInt32 position; + result = GetChildOffset(parent, grandParent, position); + if (NS_SUCCEEDED(result)) + { + if (PR_TRUE==insertAfter) + { + if (gNoisy) {printf("insertAfter=PR_TRUE, incr. position\n"); } + position++; + } + if (gNoisy) { + printf("* inserting node %p in grandparent %p at offset %d\n", + newMiddleNode.get(), grandParent.get(), position); + } + result = nsEditor::InsertNode(newMiddleNode, grandParent, position); + if (gNoisy) {DebugDumpContent(); } // DEBUG + if (NS_SUCCEEDED(result)) + { + PRBool hasChildren=PR_TRUE; + parent->HasChildNodes(&hasChildren); + if (PR_FALSE==hasChildren) { + if (gNoisy) { printf("* deleting empty style node %p\n", parent.get());} + result = nsEditor::DeleteNode(parent); + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + } + } + } + } + } + } + } + break; + } + } + } + } + } + } + return result; +} + +/* this should only get called if the only intervening nodes are inline style nodes */ +NS_IMETHODIMP +nsHTMLEditor::RemoveTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute) +{ + if (gNoisy) { printf("nsTextEditor::RemoveTextPropertiesForNodesWithSameParent\n"); } + nsresult result=NS_OK; + PRInt32 startOffset = aStartOffset; + PRInt32 endOffset; + nsCOMPtrnodeAsChar; + nsCOMPtrparentNode = do_QueryInterface(aParent); + + // remove aPropName from all intermediate nodes + nsCOMPtrsiblingNode; + nsCOMPtrnextSiblingNode; // temp to hold the next node in the list + result = aStartNode->GetNextSibling(getter_AddRefs(siblingNode)); + while (siblingNode && NS_SUCCEEDED(result)) + { + // get next sibling right away, before we move siblingNode! + siblingNode->GetNextSibling(getter_AddRefs(nextSiblingNode)); + if (aEndNode==siblingNode.get()) { // found the end node, handle that below + break; + } + else + { // found a sibling node between aStartNode and aEndNode, remove the style node + PRUint32 childCount=0; + nodeAsChar = do_QueryInterface(siblingNode); + if (nodeAsChar) { + nodeAsChar->GetLength(&childCount); + } + else + { + nsCOMPtrgrandChildNodes; + result = siblingNode->GetChildNodes(getter_AddRefs(grandChildNodes)); + if (NS_SUCCEEDED(result) && grandChildNodes) { + grandChildNodes->GetLength(&childCount); + } + if (0==childCount) + { // node has no children + // XXX: for now, I think that's ok. just pass in 0 + } + } + if (NS_SUCCEEDED(result)) { + siblingNode->GetParentNode(getter_AddRefs(parentNode)); + result = RemoveTextPropertiesForNode(siblingNode, parentNode, 0, childCount, aPropName, aAttribute); + } + } + siblingNode = do_QueryInterface(nextSiblingNode); + } + if (NS_SUCCEEDED(result)) + { + // remove aPropName from aStartNode + //nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(aStartNode); + if (nodeAsChar) { + nodeAsChar->GetLength((PRUint32 *)&endOffset); + } + else + { + if (gNoisy) { printf("not yet supported\n");} + return NS_ERROR_NOT_IMPLEMENTED; + } + result = aStartNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_SUCCEEDED(result)) { + result = RemoveTextPropertiesForNode(aStartNode, parentNode, startOffset, endOffset, aPropName, aAttribute); + } + } + if (NS_SUCCEEDED(result)) + { + // remove aPropName from the end node + startOffset = 0; + endOffset = aEndOffset; + result = aEndNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_SUCCEEDED(result)) { + result = RemoveTextPropertiesForNode(aEndNode, parentNode, startOffset, endOffset, aPropName, aAttribute); + } + } + return result; } NS_IMETHODIMP -nsHTMLEditor::StopLogging() +nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute) { - return nsTextEditor::StopLogging(); + if (gNoisy) { printf("start nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents\n"); } + nsresult result=NS_OK; + if (!aStartNode || !aEndNode || !aParent || !aPropName) + return NS_ERROR_NULL_POINTER; + + PRInt32 rangeStartOffset = aStartOffset; // used to construct a range for the nodes between + PRInt32 rangeEndOffset = aEndOffset; // aStartNode and aEndNode after we've processed those endpoints + + // temporary state variables + PRBool textPropertySet; + nsCOMPtrresultNode; + + // delete the style node for the text in the start parent + nsCOMPtrstartNode = do_QueryInterface(aStartNode); // use computed endpoint based on end points passed in + nsCOMPtrendNode = do_QueryInterface(aEndNode); // use computed endpoint based on end points passed in + nsCOMPtrparent; // the parent of the node we're operating on + PRBool skippedStartNode = PR_FALSE; // we skip the start node if aProp is not set on it + PRUint32 count; + nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(startNode); + if (nodeAsChar) + { + result = aStartNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(result)) { return result; } + nodeAsChar->GetLength(&count); + if ((PRUint32)aStartOffset!=count) + { // only do this if at least one child is selected + IsTextPropertySetByContent(aStartNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); + if (PR_TRUE==textPropertySet) + { + result = RemoveTextPropertiesForNode(aStartNode, parent, aStartOffset, count, aPropName, aAttribute); + if (0!=aStartOffset) { + rangeStartOffset = 0; // we split aStartNode at aStartOffset and it is the right node now + } + } + else + { + skippedStartNode = PR_TRUE; + if (gNoisy) { printf("skipping start node because property not set\n"); } + } + } + else + { + skippedStartNode = PR_TRUE; + if (gNoisy) { printf("skipping start node because aStartOffset==count\n"); } + } + } + else + { + startNode = GetChildAt(aStartNode, aStartOffset); + parent = do_QueryInterface(aStartNode); + if (!startNode || !parent) {return NS_ERROR_NULL_POINTER;} + IsTextPropertySetByContent(startNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); + if (PR_TRUE==textPropertySet) + { + result = RemoveTextPropertiesForNode(startNode, parent, aStartOffset, aStartOffset+1, aPropName, aAttribute); + } + else + { + skippedStartNode = PR_TRUE; + if (gNoisy) { printf("skipping start node because property not set\n"); } + } + } + + // delete the style node for the text in the end parent + if (NS_SUCCEEDED(result)) + { + nodeAsChar = do_QueryInterface(endNode); + if (nodeAsChar) + { + result = endNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(result)) { return result; } + nodeAsChar->GetLength(&count); + if (aEndOffset!=0) + { // only do this if at least one child is selected + IsTextPropertySetByContent(endNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); + if (PR_TRUE==textPropertySet) + { + result = RemoveTextPropertiesForNode(endNode, parent, 0, aEndOffset, aPropName, aAttribute); + if (0!=aEndOffset) { + rangeEndOffset = 0; // we split endNode at aEndOffset and it is the right node now + } + } + else { if (gNoisy) { printf("skipping end node because aProperty not set.\n"); } } + } + else { if (gNoisy) { printf("skipping end node because aEndOffset==0\n"); } } + } + else + { + endNode = GetChildAt(aEndNode, aEndOffset); + parent = do_QueryInterface(aEndNode); + if (!endNode || !parent) {return NS_ERROR_NULL_POINTER;} + result = RemoveTextPropertiesForNode(endNode, parent, aEndOffset, aEndOffset+1, aPropName, aAttribute); + } + } + + // remove aPropName style nodes for all the content between the start and end nodes + if (NS_SUCCEEDED(result)) + { + // build our own range now, because the endpoints may have shifted during shipping + nsCOMPtr range; + result = nsComponentManager::CreateInstance(kCRangeCID, + nsnull, + nsIDOMRange::GetIID(), + getter_AddRefs(range)); + if (NS_FAILED(result)) { return result; } + if (!range) { return NS_ERROR_NULL_POINTER; } + // compute the start node + if (PR_TRUE==skippedStartNode) + { + nsCOMPtrtempNode = do_QueryInterface(startNode); + nsEditor::GetNextNode(startNode, PR_TRUE, getter_AddRefs(tempNode)); + startNode = do_QueryInterface(tempNode); + } + range->SetStart(startNode, rangeStartOffset); + range->SetEnd(endNode, rangeEndOffset); + if (gNoisy) + { + printf("created range [(%p,%d), (%p,%d)]\n", + startNode.get(), rangeStartOffset, + endNode.get(), rangeEndOffset); + } + + nsVoidArray nodeList; + nsCOMPtriter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + nsCOMPtrstartContent; + startContent = do_QueryInterface(startNode); + nsCOMPtrendContent; + endContent = do_QueryInterface(endNode); + if (startContent && endContent) + { + iter->Init(range); + nsCOMPtr content; + iter->CurrentNode(getter_AddRefs(content)); + nsAutoString propName; // the property we are removing + aPropName->ToString(propName); + while (NS_COMFALSE == iter->IsDone()) + { + if ((content.get() != startContent.get()) && + (content.get() != endContent.get())) + { + nsCOMPtrelement; + element = do_QueryInterface(content); + if (element) + { + nsString tag; + element->GetTagName(tag); + if (propName.EqualsIgnoreCase(tag)) + { + if (-1==nodeList.IndexOf(content.get())) { + nodeList.AppendElement((void *)(content.get())); + } + } + } + } + // note we don't check the result, we just rely on iter->IsDone + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + } + } + + // now delete all the style nodes we found + if (NS_SUCCEEDED(result)) + { + nsIContent *contentPtr; + contentPtr = (nsIContent*)(nodeList.ElementAt(0)); + while (NS_SUCCEEDED(result) && contentPtr) + { + nsCOMPtrstyleNode; + styleNode = do_QueryInterface(contentPtr); + // promote the children of styleNode + nsCOMPtrparentNode; + result = styleNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_SUCCEEDED(result) && parentNode) + { + PRInt32 position; + result = GetChildOffset(styleNode, parentNode, position); + if (NS_SUCCEEDED(result)) + { + nsCOMPtrpreviousSiblingNode; + nsCOMPtrchildNode; + result = styleNode->GetLastChild(getter_AddRefs(childNode)); + while (NS_SUCCEEDED(result) && childNode) + { + childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); + // explicitly delete of childNode from styleNode + // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! + result = nsEditor::DeleteNode(childNode); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::InsertNode(childNode, parentNode, position); + if (gNoisy) + { + printf("deleted next sibling node %p\n", childNode.get()); + DebugDumpContent(); // DEBUG + } + } + childNode = do_QueryInterface(previousSiblingNode); + } // end while loop + // delete styleNode + result = nsEditor::DeleteNode(styleNode); + if (gNoisy) + { + printf("deleted style node %p\n", styleNode.get()); + DebugDumpContent(); // DEBUG + } + } + } + + // get next content ptr + nodeList.RemoveElementAt(0); + contentPtr = (nsIContent*)(nodeList.ElementAt(0)); + } + } + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result)) + { + selection->Collapse(startNode, rangeStartOffset); + selection->Extend(endNode, rangeEndOffset); + if (gNoisy) { printf("RTPFNWDP set selection.\n"); } + } + } + if (gNoisy) + { + printf("end nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents, dumping content...\n"); + DebugDumpContent(); + } + + return result; } diff --git a/editor/base/nsHTMLEditor.h b/editor/base/nsHTMLEditor.h index 38807d259708..51b1a0e0fdc6 100644 --- a/editor/base/nsHTMLEditor.h +++ b/editor/base/nsHTMLEditor.h @@ -19,21 +19,33 @@ #ifndef nsHTMLEditor_h__ #define nsHTMLEditor_h__ -#include "nsITextEditor.h" -#include "nsTextEditor.h" -#include "nsIHTMLEditor.h" #include "nsCOMPtr.h" + +#include "nsIHTMLEditor.h" +#include "nsITableEditor.h" +#include "nsIEditorMailSupport.h" +#include "nsIEditorStyleSheets.h" + +#include "nsEditor.h" +#include "nsIDOMElement.h" #include "nsIDOMEventListener.h" #include "nsITableLayout.h" +#include "TypeInState.h" +#include "nsEditRules.h" + /** * The HTML editor implementation.
* Use to edit HTML document represented as a DOM tree. */ -class nsHTMLEditor : public nsTextEditor, public nsIHTMLEditor +class nsHTMLEditor : public nsEditor, + public nsIHTMLEditor, + public nsIEditorMailSupport, + public nsITableEditor, + public nsIEditorStyleSheets { - typedef enum {eNoOp=0, eReplaceParent=1, eInsertParent=2} BlockTransformationType; + typedef enum {eNoOp, eReplaceParent=1, eInsertParent=2} BlockTransformationType; public: // see nsIHTMLEditor for documentation @@ -43,139 +55,77 @@ public: // another class. Only the base class should use NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS_INHERITED - nsHTMLEditor(); - virtual ~nsHTMLEditor(); + nsHTMLEditor(); + virtual ~nsHTMLEditor(); -//Initialization - NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell); + /* ------------ nsIHTMLEditor methods -------------- */ -//============================================================================ -// Methods that are duplicates of nsTextEditor -- exposed here for convenience + NS_IMETHOD GetDocumentLength(PRInt32 *aCount); + NS_IMETHOD SetMaxTextLength(PRInt32 aMaxTextLength); + NS_IMETHOD GetMaxTextLength(PRInt32& aMaxTextLength); -// Editing Operations - NS_IMETHOD SetTextProperty(nsIAtom *aProperty, + + NS_IMETHOD SetInlineProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue); - NS_IMETHOD GetTextProperty(nsIAtom *aProperty, - const nsString *aAttribute, const nsString *aValue, + + NS_IMETHOD GetInlineProperty(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, PRBool &aFirst, PRBool &aAny, PRBool &aAll); - NS_IMETHOD RemoveTextProperty(nsIAtom *aProperty, const nsString *aAttribute); - NS_IMETHOD DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction); - NS_IMETHOD InsertText(const nsString& aStringToInsert); + + NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute); + NS_IMETHOD InsertBreak(); - NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); - -// Transaction control - NS_IMETHOD EnableUndo(PRBool aEnable); - NS_IMETHOD Undo(PRUint32 aCount); - NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo); - NS_IMETHOD Redo(PRUint32 aCount); - NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo); - NS_IMETHOD BeginTransaction(); - NS_IMETHOD EndTransaction(); - -// Selection and navigation - NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD SelectAll(); - NS_IMETHOD BeginningOfDocument(); - NS_IMETHOD EndOfDocument(); - NS_IMETHOD ScrollUp(nsIAtom *aIncrement); - NS_IMETHOD ScrollDown(nsIAtom *aIncrement); - NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin); - -// file handling - NS_IMETHOD Save(); - NS_IMETHOD SaveAs(PRBool aSavingCopy); - -// cut, copy & paste - NS_IMETHOD Cut(); - NS_IMETHOD Copy(); - NS_IMETHOD Paste(); - NS_IMETHOD PasteAsQuotation(); - NS_IMETHOD PasteAsCitedQuotation(const nsString& aCitation); - NS_IMETHOD InsertAsQuotation(const nsString& aQuotedText); - NS_IMETHOD InsertAsCitedQuotation(const nsString& aQuotedText, - const nsString& aCitation); - -// Input/Output - NS_IMETHOD InsertHTML(const nsString& aInputString); - - NS_IMETHOD BeginComposition(void); - NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRange, nsTextEventReply* aReply); - NS_IMETHOD EndComposition(void); - - NS_IMETHOD OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags); - NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags); - -// Miscellaneous - NS_IMETHOD ApplyStyleSheet(const nsString& aURL); - -// Logging methods - - NS_IMETHOD StartLogging(nsIFileSpec *aLogFile); - NS_IMETHOD StopLogging(); - -// End of methods implemented in nsEditor -//============================================================= -// HTML Editing methods - - // This sets background on the appropriate container element (table, cell,) - // or calls into nsTextEditor to set the page background - NS_IMETHOD SetBackgroundColor(const nsString& aColor); - NS_IMETHOD SetBodyAttribute(const nsString& aAttr, const nsString& aValue); - NS_IMETHOD GetParagraphStyle(nsStringArray *aTagList); - NS_IMETHOD AddBlockParent(nsString& aParentTag); - NS_IMETHOD ReplaceBlockParent(nsString& aParentTag); - NS_IMETHOD RemoveParagraphStyle(); - NS_IMETHOD RemoveParent(const nsString &aParentTag); + NS_IMETHOD InsertText(const nsString& aStringToInsert); + NS_IMETHOD InsertHTML(const nsString &aInputString); + NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection); + + NS_IMETHOD DeleteSelection(ESelectionCollapseDirection aAction); + NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode); + NS_IMETHOD SelectElement(nsIDOMElement* aElement); + NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement); NS_IMETHOD GetParagraphFormat(nsString& aParagraphFormat); NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat); + NS_IMETHOD GetParagraphStyle(nsStringArray *aTagList); + NS_IMETHOD RemoveParagraphStyle(); + + NS_IMETHOD AddBlockParent(nsString& aParentTag); + NS_IMETHOD ReplaceBlockParent(nsString& aParentTag); + NS_IMETHOD RemoveParent(const nsString &aParentTag); + + NS_IMETHOD InsertList(const nsString& aListType); NS_IMETHOD Indent(const nsString& aIndent); NS_IMETHOD Align(const nsString& aAlign); - NS_IMETHOD InsertList(const nsString& aListType); - -// MHTML helper methods - NS_IMETHOD GetEmbeddedObjects(nsISupportsArray** aNodeList); NS_IMETHOD GetElementOrParentByTagName(const nsString& aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn); NS_IMETHOD GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn); NS_IMETHOD CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement** aReturn); - NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection); + NS_IMETHOD SaveHLineSettings(nsIDOMElement* aElement); NS_IMETHOD InsertLinkAroundSelection(nsIDOMElement* aAnchorElement); - NS_IMETHOD SelectElement(nsIDOMElement* aElement); - NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement); - // Return TRUE if aElement is a table-related elemet and caret was set - PRBool SetCaretInTableCell(nsIDOMElement* aElement); - PRBool IsElementInBody(nsIDOMElement* aElement); -// Table Editing (implemented in EditTable.cpp) - // Helper used to get nsITableLayout interface for methods implemented in nsTableFrame - NS_IMETHOD GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **tableLayoutObject); - /** Get the row an column index from the layout's cellmap */ - NS_IMETHOD GetCellIndexes(nsIDOMElement *aCell, PRInt32 & aRowIndex, PRInt32 &aColIndex); - /** Get the number of rows and columns in a table from the layout's cellmap */ - NS_IMETHOD GetTableSize(nsIDOMElement *aTable, PRInt32& aRowCount, PRInt32& aColCount); + /* ------------ nsIEditorStyleSheets methods -------------- */ - /* Get cell at a cellmap location. Returns NS_TABLELAYOUT_CELL_NOT_FOUND if past end of row or col */ - NS_IMETHOD GetCellAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell); - /* Get cell and associated data */ - NS_IMETHOD GetCellDataAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell, - PRInt32& aStartRowIndex, PRInt32& aStartColIndex, - PRInt32& aRowSpan, PRInt32& aColSpan, PRBool& aIsSelected); + NS_IMETHOD ApplyStyleSheet(const nsString& aURL); + NS_IMETHOD AddStyleSheet(nsICSSStyleSheet* aSheet); + NS_IMETHOD RemoveStyleSheet(nsICSSStyleSheet* aSheet); + + /* ------------ nsIEditorMailSupport methods -------------- */ + + NS_IMETHOD GetBodyWrapWidth(PRInt32 *aWrapColumn); + NS_IMETHOD SetBodyWrapWidth(PRInt32 aWrapColumn); + NS_IMETHOD PasteAsQuotation(); + NS_IMETHOD InsertAsQuotation(const nsString& aQuotedText); + NS_IMETHOD PasteAsCitedQuotation(const nsString& aCitation); + NS_IMETHOD InsertAsCitedQuotation(const nsString& aQuotedText, const nsString& aCitation); + NS_IMETHOD GetEmbeddedObjects(nsISupportsArray** aNodeList); + + + /* ------------ nsITableEditor methods -------------- */ NS_IMETHOD InsertTableCell(PRInt32 aNumber, PRBool aAfter); NS_IMETHOD InsertTableColumn(PRInt32 aNumber, PRBool aAfter); @@ -185,15 +135,85 @@ public: NS_IMETHOD DeleteTableColumn(PRInt32 aNumber); NS_IMETHOD DeleteTableRow(PRInt32 aNumber); NS_IMETHOD JoinTableCells(); - - /** Make table "rectangular" -- fill in all missing cellmap locations - * If aTable is null, it uses table enclosing the selection anchor - */ NS_IMETHOD NormalizeTable(nsIDOMElement *aTable); + NS_IMETHOD GetCellIndexes(nsIDOMElement *aCell, PRInt32& aRowIndex, PRInt32& aColIndex); + NS_IMETHOD GetTableSize(nsIDOMElement *aTable, PRInt32& aRowCount, PRInt32& aColCount); + NS_IMETHOD GetCellAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell); + NS_IMETHOD GetCellDataAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell, + PRInt32& aStartRowIndex, PRInt32& aStartColIndex, + PRInt32& aRowSpan, PRInt32& aColSpan, PRBool& aIsSelected); + + +// Selection and navigation + /* obsolete + NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD ScrollUp(nsIAtom *aIncrement); + NS_IMETHOD ScrollDown(nsIAtom *aIncrement); + NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin); + */ + + /* miscellaneous */ + // This sets background on the appropriate container element (table, cell,) + // or calls into nsTextEditor to set the page background + NS_IMETHOD SetBackgroundColor(const nsString& aColor); + NS_IMETHOD SetBodyAttribute(const nsString& aAttr, const nsString& aValue); + + + + /* ------------ Overrides of nsEditor interface methods -------------- */ + + NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, PRUint32 aFlags); + + NS_IMETHOD GetFlags(PRUint32 *aFlags); + NS_IMETHOD SetFlags(PRUint32 aFlags); + + NS_IMETHOD Cut(); + NS_IMETHOD Copy(); + NS_IMETHOD Paste(); + + NS_IMETHOD OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags); + + NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags); + + NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); + +protected: + + virtual void InitRules(); + + NS_IMETHOD GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject); + + NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode); + + /* StyleSheet load callback */ + static void ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData); + + /* remove the old style sheet, and apply the supplied one */ + NS_IMETHOD ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet); + + + // Return TRUE if aElement is a table-related elemet and caret was set + PRBool SetCaretInTableCell(nsIDOMElement* aElement); + PRBool IsElementInBody(nsIDOMElement* aElement); + +// Table Editing (implemented in EditTable.cpp) + + // Helper used to get nsITableLayout interface for methods implemented in nsTableFrame + NS_IMETHOD GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **tableLayoutObject); // Table utilities -// All of the above need to get the same basic context data + // All of the above need to get the same basic context data NS_IMETHOD GetCellContext(nsCOMPtr &aSelection, nsCOMPtr &aTable, nsCOMPtr &aCell, nsCOMPtr &aCellParent, PRInt32& aCellOffset, @@ -204,15 +224,10 @@ public: // Setting caret to a logical place can get tricky, // especially after deleting table stuff - typedef enum { ePreviousColumn=0, ePreviousRow } SetCaretSearchDirection; + typedef enum { ePreviousColumn, ePreviousRow } SetCaretSearchDirection; + NS_IMETHOD SetCaretAfterTableEdit(nsIDOMElement* aTable, PRInt32 aCol, PRInt32 aRow, SetCaretSearchDirection aDirection); - -protected: - -// rules initialization - - virtual void InitRules(); - + NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag, BlockTransformationType aTranformation); @@ -241,14 +256,178 @@ protected: NS_IMETHOD IsSubordinateBlock(nsString &aTag, PRBool &aIsTag); -// EVENT LISTENERS AND COMMAND ROUTING NEEDS WORK -// For now, the listners are tied to the nsTextEditor class -// -// nsCOMPtr mKeyListenerP; -// nsCOMPtr mMouseListenerP; + /** content-based query returns PR_TRUE if effects aNode + * If contains aNode, + * but also contains aNode and the second is + * more deeply nested than the first, then the first does not effect aNode. + * + * @param aNode The target of the query + * @param aProperty The property that we are querying for + * @param aAttribute The attribute of aProperty, example: color in + * May be null. + * @param aValue The value of aAttribute, example: blue in + * May be null. Ignored if aAttribute is null. + * @param aIsSet [OUT] PR_TRUE if effects aNode. + * @param aStyleNode [OUT] set to the node representing , if found. + * null if aIsSet is returned as PR_FALSE; + */ + virtual void IsTextPropertySetByContent(nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aIsSet, + nsIDOMNode **aStyleNode) const; - // this overrides the base class implementation. It is not exported in nsIHTMLEditor. - NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); + /** 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. + */ + virtual void IsTextStyleSet(nsIStyleContext *aSC, + nsIAtom *aProperty, + const nsString *aAttributes, + PRBool &aIsSet) const; + + /** Moves the content between (aNode, aStartOffset) and (aNode, aEndOffset) + * into aNewParentNode, splitting aNode as necessary to maintain the relative + * position of all leaf content. + * @param aNode The node whose content we're repositioning. + * aNode can be either a text node or a container node. + * @param aNewParentNode The node that will be the repositioned contents' parent. + * The caller is responsible for allocating aNewParentNode + * @param aStartOffset The start offset of the content of aNode + * @param aEndOffset The end offset of the content of aNode. + */ + NS_IMETHOD MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, + nsIDOMNode *aNewParentNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset); + + /** Moves the content between (aStartNode, aStartOffset) and (aEndNode, aEndOffset) + * into aNewParentNode, splitting aStartNode and aEndNode as necessary to maintain + * the relative position of all leaf content. + * The content between the two endpoints MUST be "contiguous" in the sense that + * it is all in the same block. Another way of saying this is all content nodes + * between aStartNode and aEndNode must be inline. + * @see IntermediateNodesAreInline + * + * @param aStartNode The left node, can be either a text node or a container node. + * @param aStartOffset The start offset in the content of aStartNode + * @param aEndNode The right node, can be either a text node or a container node. + * @param aEndOffset The end offset in the content of aEndNode. + * @param aGrandParentNode The common ancestor of aStartNode and aEndNode. + * aGrandParentNode will be the parent of aNewParentNode. + * @param aNewParentNode The node that will be the repositioned contents' parent. + * The caller is responsible for allocating aNewParentNode + */ + NS_IMETHOD MoveContiguousContentIntoNewParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aGrandParentNode, + nsIDOMNode *aNewParentNode); + + + NS_IMETHOD SetTextPropertiesForNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue); + + NS_IMETHOD SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue); + + NS_IMETHOD SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, + nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue); + + NS_IMETHOD RemoveTextPropertiesForNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aPropName, + const nsString *aAttribute); + + NS_IMETHOD RemoveTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute); + + NS_IMETHOD RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute); + + NS_IMETHOD SetTypeInStateForProperty(TypeInState &aTypeInState, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue); + + void GetTextSelectionOffsetsForRange(nsIDOMSelection *aSelection, + nsIDOMNode **aParent, + PRInt32 &aStartOffset, + PRInt32 &aEndOffset); + + void ResetTextSelectionForRange(nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIDOMSelection *aSelection); + + /** returns the absolute position of the end points of aSelection + * in the document as a text stream. + */ + nsresult GetTextSelectionOffsets(nsIDOMSelection *aSelection, + PRInt32 &aStartOffset, + PRInt32 &aEndOffset); + + // Methods for handling plaintext quotations + NS_IMETHOD PasteAsPlaintextQuotation(); + NS_IMETHOD InsertAsPlaintextQuotation(const nsString& aQuotedText); + + // I hate seeing nsCOMPtr return types. + nsCOMPtr FindPreElement(); + + TypeInState *GetTypeInState(); + + /** simple utility to handle any error with event listener allocation or registration */ + void HandleEventListenerError(); + +// Data members +protected: + + TypeInState* mTypeInState; + nsEditRules* mRules; + nsCOMPtr mKeyListenerP; + nsCOMPtr mMouseListenerP; + nsCOMPtr mTextListenerP; + nsCOMPtr mCompositionListenerP; + nsCOMPtr mDragListenerP; + nsCOMPtr mFocusListenerP; + PRBool mIsComposing; + PRInt32 mMaxTextLength; + PRUint32 mWrapColumn; + +// friends +friend class nsHTMLEditRules; +friend class nsTextEditRules; }; diff --git a/editor/base/nsInterfaceState.cpp b/editor/base/nsInterfaceState.cpp index 7af4a39a8e91..09a4f5ed43bd 100644 --- a/editor/base/nsInterfaceState.cpp +++ b/editor/base/nsInterfaceState.cpp @@ -30,7 +30,7 @@ #include "nsIDOMElement.h" #include "nsIDOMSelection.h" -#include "nsITextEditor.h" +#include "nsIEditor.h" #include "nsIHTMLEditor.h" #include "nsInterfaceState.h" @@ -88,7 +88,7 @@ nsInterfaceState::QueryInterface(const nsIID& aIID, void** aInstancePtr) } NS_IMETHODIMP -nsInterfaceState::Init(nsISupports* aEditor, nsIWebShell *aChromeWebShell) +nsInterfaceState::Init(nsIHTMLEditor* aEditor, nsIWebShell *aChromeWebShell) { if (!aEditor) return NS_ERROR_INVALID_ARG; @@ -153,9 +153,7 @@ nsInterfaceState::UpdateParagraphState(const char* observerName, const char* att { nsStringArray tagList; - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - htmlEditor->GetParagraphStyle(&tagList); + mEditor->GetParagraphStyle(&tagList); PRInt32 numTags = tagList.Count(); if (numTags > 0) @@ -184,18 +182,14 @@ nsInterfaceState::UpdateListState(const char* observerName, const char* tagName) editor->GetSelection(getter_AddRefs(domSelection)); } - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tagStr(tagName); - - nsCOMPtr domNode; - if (domSelection) - domSelection->GetAnchorNode(getter_AddRefs(domNode)); - - nsCOMPtr parentElement; - rv = htmlEditor->GetElementOrParentByTagName(tagStr, domNode, getter_AddRefs(parentElement)); - } + nsAutoString tagStr(tagName); + + nsCOMPtr domNode; + if (domSelection) + domSelection->GetAnchorNode(getter_AddRefs(domNode)); + + nsCOMPtr parentElement; + rv = mEditor->GetElementOrParentByTagName(tagStr, domNode, getter_AddRefs(parentElement)); return rv; } @@ -212,18 +206,7 @@ nsInterfaceState::UpdateFontFace(const char* observerName, const char* attribute nsCOMPtr styleAtom = getter_AddRefs(NS_NewAtom("font")); nsAutoString faceStr("face"); - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - { - rv = textEditor->GetTextProperty(styleAtom, &faceStr, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); - } - else - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - rv = htmlEditor->GetTextProperty(styleAtom, &faceStr, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); - } - + rv = mEditor->GetInlineProperty(styleAtom, &faceStr, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); return rv; } @@ -240,17 +223,7 @@ nsInterfaceState::UpdateTextState(const char* tagName, const char* observerName, nsCOMPtr styleAtom = getter_AddRefs(NS_NewAtom(tagName)); - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - { - rv = textEditor->GetTextProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); - } - else - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - rv = htmlEditor->GetTextProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); - } + rv = mEditor->GetInlineProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); PRBool &behaviour = allOfSelectionHasProp; // change this to alter the behaviour if (behaviour != ioState) @@ -309,7 +282,7 @@ nsInterfaceState::SetNodeAttribute(const char* nodeID, const char* attributeName } -nsresult NS_NewInterfaceState(nsISupports* aEditor, nsIWebShell* aWebShell, nsIDOMSelectionListener** aInstancePtrResult) +nsresult NS_NewInterfaceState(nsIHTMLEditor* aEditor, nsIWebShell* aWebShell, nsIDOMSelectionListener** aInstancePtrResult) { nsInterfaceState* newThang = new nsInterfaceState; if (!newThang) diff --git a/editor/base/nsInterfaceState.h b/editor/base/nsInterfaceState.h index 3203ad4c066a..abab7019140d 100644 --- a/editor/base/nsInterfaceState.h +++ b/editor/base/nsInterfaceState.h @@ -25,6 +25,8 @@ #include "nsIDocumentStateListener.h" #include "nsIWebShell.h" +class nsIHTMLEditor; + // class responsible for communicating changes in local state back to the UI. // This is currently somewhat tied to a given XUL UI implementation @@ -38,7 +40,7 @@ public: NS_DECL_ISUPPORTS - NS_IMETHOD Init(nsISupports* aEditor, nsIWebShell *aChromeWebShell); + NS_IMETHOD Init(nsIHTMLEditor* aEditor, nsIWebShell *aChromeWebShell); // nsIDOMSelectionListener interface NS_IMETHOD NotifySelectionChanged(); @@ -67,7 +69,7 @@ protected: // this class should not hold references to the editor or editorShell. Doing // so would result in cirular reference chains. - nsISupports* mEditor; // the text or HTML editor + nsIHTMLEditor* mEditor; // the HTML editor nsIWebShell* mWebShell; // web shell for the chrome area // current state @@ -83,6 +85,6 @@ protected: }; -extern "C" nsresult NS_NewInterfaceState(nsISupports* aEditor, nsIWebShell* aWebShell, nsIDOMSelectionListener** aInstancePtrResult); +extern "C" nsresult NS_NewInterfaceState(nsIHTMLEditor* aEditor, nsIWebShell* aWebShell, nsIDOMSelectionListener** aInstancePtrResult); #endif // nsInterfaceState_h__ diff --git a/editor/base/nsTextEditRules.cpp b/editor/base/nsTextEditRules.cpp index d8b06bf0b94f..ff7f3b955949 100644 --- a/editor/base/nsTextEditRules.cpp +++ b/editor/base/nsTextEditRules.cpp @@ -17,10 +17,11 @@ */ #include "nsTextEditRules.h" -#include "nsTextEditor.h" + #include "nsEditor.h" #include "PlaceholderTxn.h" #include "InsertTextTxn.h" + #include "nsCOMPtr.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" @@ -37,7 +38,7 @@ static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \ - if (mFlags & TEXT_EDITOR_FLAG_READONLY || mFlags & TEXT_EDITOR_FLAG_DISABLED) \ + if ((mFlags & nsIHTMLEditor::eEditorReadonlyMask) || (mFlags & nsIHTMLEditor::eEditorDisabledMask)) \ { \ *aCancel = PR_TRUE; \ return NS_OK; \ @@ -47,10 +48,10 @@ static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); * Constructor/Destructor ********************************************************/ -nsTextEditRules::nsTextEditRules() +nsTextEditRules::nsTextEditRules(PRUint32 aFlags) +: mEditor(nsnull) +, mFlags(aFlags) { - mEditor = nsnull; - mFlags=0; } nsTextEditRules::~nsTextEditRules() @@ -64,10 +65,11 @@ nsTextEditRules::~nsTextEditRules() ********************************************************/ NS_IMETHODIMP -nsTextEditRules::Init(nsIEditor *aEditor) +nsTextEditRules::Init(nsHTMLEditor *aEditor) { if (!aEditor) { return NS_ERROR_NULL_POINTER; } - mEditor = (nsTextEditor*)aEditor; // we hold a non-refcounted reference back to our editor + + mEditor = aEditor; // we hold a non-refcounted reference back to our editor nsCOMPtr selection; mEditor->GetSelection(getter_AddRefs(selection)); NS_ASSERTION(selection, "editor cannot get selection"); @@ -92,19 +94,19 @@ nsTextEditRules::SetFlags(PRUint32 aFlags) // a style attribute on it, don't know why. // SetFlags() is really meant to only be called once // and at editor init time. - if (aFlags & TEXT_EDITOR_FLAG_PLAINTEXT) + if (aFlags & nsIHTMLEditor::eEditorPlaintextMask) { - if (!(mFlags & TEXT_EDITOR_FLAG_PLAINTEXT)) + if (!(mFlags & nsIHTMLEditor::eEditorPlaintextMask)) { // we are converting TO a plaintext editor // put a "white-space: pre" style on the body - nsCOMPtr bodyElement; - nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_SUCCEEDED(res) && bodyElement) - { - // not going through the editor to do this. - bodyElement->SetAttribute("style", "white-space: pre"); - } + nsCOMPtr bodyElement; + nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res) && bodyElement) + { + // not going through the editor to do this. + bodyElement->SetAttribute("style", "white-space: pre"); + } } } mFlags = aFlags; @@ -194,7 +196,9 @@ nsTextEditRules::DidDoAction(nsIDOMSelection *aSelection, nsresult nsTextEditRules::WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel) { - if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } + if (!aSelection || !aCancel) + return NS_ERROR_NULL_POINTER; + CANCEL_OPERATION_IF_READONLY_OR_DISABLED // initialize out param @@ -223,7 +227,7 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } CANCEL_OPERATION_IF_READONLY_OR_DISABLED - if (mFlags & TEXT_EDITOR_FLAG_SINGLELINE) { + if (mFlags & nsIHTMLEditor::eEditorSingleLineMask) { *aCancel = PR_TRUE; } else { @@ -256,7 +260,7 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, nsString inString = *aInString; // we might want to mutate the input // before setting the output, do that in a local var - if ((-1 != aMaxLength) && (mFlags&TEXT_EDITOR_FLAG_PLAINTEXT)) + if ((-1 != aMaxLength) && (mFlags & nsIHTMLEditor::eEditorPlaintextMask)) { // Get the current text length. // Get the length of inString. @@ -278,14 +282,14 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, if (selectionLength<0) { selectionLength *= (-1); } PRInt32 resultingDocLength = docLength - selectionLength; if (resultingDocLength >= aMaxLength) - { - *aOutString = ""; - *aCancel = PR_TRUE; - return result; - } - else - { - PRInt32 inCount = inString.Length(); + { + *aOutString = ""; + *aCancel = PR_TRUE; + return result; + } + else + { + PRInt32 inCount = inString.Length(); if ((inCount+resultingDocLength) > aMaxLength) { inString.Truncate(aMaxLength-resultingDocLength); @@ -298,7 +302,7 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, // initialize out params *aCancel = PR_FALSE; - if (mFlags&TEXT_EDITOR_FLAG_PASSWORD) + if (mFlags & nsIHTMLEditor::eEditorPasswordMask) { // manage the password buffer PRInt32 start, end; @@ -589,7 +593,7 @@ nsTextEditRules::InsertStyleAndNewTextNode(nsIDOMNode *aParentNode, nsIAtom *aTa result = mEditor->CreateNode(tag, aParentNode, 0, getter_AddRefs(newStyleNode)); if (NS_SUCCEEDED(result)) { - result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newStyleNode, 0, getter_AddRefs(newTextNode)); + result = mEditor->CreateNode(nsEditor::GetTextNodeTag(), newStyleNode, 0, getter_AddRefs(newTextNode)); if (NS_SUCCEEDED(result)) { if (aSelection) { @@ -606,7 +610,7 @@ nsTextEditRules::WillSetTextProperty(nsIDOMSelection *aSelection, PRBool *aCance nsresult result = NS_OK; // XXX: should probably return a success value other than NS_OK that means "not allowed" - if (TEXT_EDITOR_FLAG_PLAINTEXT & mFlags) { + if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) { *aCancel = PR_TRUE; } return result; @@ -624,7 +628,7 @@ nsTextEditRules::WillRemoveTextProperty(nsIDOMSelection *aSelection, PRBool *aCa nsresult result = NS_OK; // XXX: should probably return a success value other than NS_OK that means "not allowed" - if (TEXT_EDITOR_FLAG_PLAINTEXT & mFlags) { + if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) { *aCancel = PR_TRUE; } return result; @@ -638,7 +642,7 @@ nsTextEditRules::DidRemoveTextProperty(nsIDOMSelection *aSelection, nsresult aRe nsresult nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ECollapsedSelectionAction aCollapsedAction, + nsIEditor::ESelectionCollapseDirection aCollapsedAction, PRBool *aCancel) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } @@ -652,17 +656,17 @@ nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, *aCancel = PR_TRUE; return NS_OK; } - if (mFlags&TEXT_EDITOR_FLAG_PASSWORD) + if (mFlags & nsIHTMLEditor::eEditorPasswordMask) { // manage the password buffer PRInt32 start, end; mEditor->GetTextSelectionOffsets(aSelection, start, end); if (end==start) { // collapsed selection - if (nsIEditor::eDeleteLeft==aCollapsedAction && 0newTNode; - result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), mBogusNode, 0, + result = mEditor->CreateNode(nsEditor::GetTextNodeTag(), mBogusNode, 0, getter_AddRefs(newTNode)); if ((NS_SUCCEEDED(result)) && newTNode) { diff --git a/editor/base/nsTextEditRules.h b/editor/base/nsTextEditRules.h index 661b4d28276a..9b3eb8e75d76 100644 --- a/editor/base/nsTextEditRules.h +++ b/editor/base/nsTextEditRules.h @@ -19,10 +19,12 @@ #ifndef nsTextEditRules_h__ #define nsTextEditRules_h__ -#include "nsIEditor.h" -#include "nsEditRules.h" #include "nsCOMPtr.h" + +#include "nsHTMLEditor.h" #include "nsIDOMNode.h" + +#include "nsEditRules.h" #include "TypeInState.h" class PlaceholderTxn; @@ -43,11 +45,11 @@ class nsTextEditRules : public nsEditRules { public: - nsTextEditRules(); - virtual ~nsTextEditRules(); + nsTextEditRules(PRUint32 aFlags); + virtual ~nsTextEditRules(); // nsEditRules methods - NS_IMETHOD Init(nsIEditor *aEditor); + NS_IMETHOD Init(nsHTMLEditor *aEditor); NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel); NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); NS_IMETHOD GetFlags(PRUint32 *aFlags); @@ -96,10 +98,10 @@ protected: nsresult DidInsert(nsIDOMSelection *aSelection, nsresult aResult); nsresult WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ECollapsedSelectionAction aCollapsedAction, + nsIEditor::ESelectionCollapseDirection aCollapsedAction, PRBool *aCancel); nsresult DidDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ECollapsedSelectionAction aCollapsedAction, + nsIEditor::ESelectionCollapseDirection aCollapsedAction, nsresult aResult); nsresult WillSetTextProperty(nsIDOMSelection *aSelection, PRBool *aCancel); @@ -154,7 +156,7 @@ protected: nsresult PinSelectionInPRE(nsIDOMSelection *aSelection); // data - nsTextEditor *mEditor; // note that we do not refcount the editor + 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 PRUint32 mFlags; @@ -173,7 +175,7 @@ class nsTextRulesInfo : public nsRulesInfo outString(0), typeInState(), maxLength(-1), - collapsedAction(nsIEditor::eDeleteRight) + collapsedAction(nsIEditor::eDeleteNext) {}; virtual ~nsTextRulesInfo() {}; @@ -186,7 +188,7 @@ class nsTextRulesInfo : public nsRulesInfo PRInt32 maxLength; // kDeleteSelection - nsIEditor::ECollapsedSelectionAction collapsedAction; + nsIEditor::ESelectionCollapseDirection collapsedAction; // kMakeList PRBool bOrdered; diff --git a/editor/composer/src/nsEditorShell.cpp b/editor/composer/src/nsEditorShell.cpp index ee4483b41e09..9ca61c98ce84 100644 --- a/editor/composer/src/nsEditorShell.cpp +++ b/editor/composer/src/nsEditorShell.cpp @@ -73,8 +73,12 @@ #include "nsIDOMDocument.h" #include "nsIEditor.h" -#include "nsITextEditor.h" #include "nsIHTMLEditor.h" +#include "nsIEditorStyleSheets.h" +#include "nsIEditorMailSupport.h" +#include "nsITableEditor.h" +#include "nsIEditorLogging.h" + #include "nsEditorCID.h" #include "nsIComponentManager.h" @@ -94,9 +98,7 @@ /* Define Class IDs */ static NS_DEFINE_IID(kAppShellServiceCID, NS_APPSHELL_SERVICE_CID); -static NS_DEFINE_IID(kEditorAppCoreCID, NS_EDITORAPPCORE_CID); static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); -static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); static NS_DEFINE_CID(kCTextServicesDocumentCID, NS_TEXTSERVICESDOCUMENT_CID); static NS_DEFINE_CID(kCSpellCheckerCID, NS_SPELLCHECKER_CID); static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); @@ -317,54 +319,39 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell) nsresult err = NS_OK; - if (mEditorTypeString == "text") + nsCOMPtr editor; + err = nsComponentManager::CreateInstance(kHTMLEditorCID, nsnull, nsIEditor::GetIID(), getter_AddRefs(editor)); + if(!editor) + err = NS_ERROR_OUT_OF_MEMORY; + + if (NS_SUCCEEDED(err)) { - nsCOMPtr editor; - err = nsComponentManager::CreateInstance(kTextEditorCID, nsnull, nsITextEditor::GetIID(), getter_AddRefs(editor)); - if(!editor) - err = NS_ERROR_OUT_OF_MEMORY; - - if (NS_SUCCEEDED(err)) + if (mEditorTypeString == "text") { - err = editor->Init(aDoc, aPresShell); - if (NS_SUCCEEDED(err) && editor) - { - mEditor = do_QueryInterface(editor); // this does the addref that is the owning reference - mEditorType = ePlainTextEditorType; - - // and set the initial wrap column - editor->SetBodyWrapWidth(mWrapColumn); - } - } - } - else if (mEditorTypeString == "html" || mEditorTypeString == "") // empty string default to HTML editor - { - nsCOMPtr editor; - err = nsComponentManager::CreateInstance(kHTMLEditorCID, nsnull, nsIHTMLEditor::GetIID(), getter_AddRefs(editor)); - if(!editor) - err = NS_ERROR_OUT_OF_MEMORY; - - if (NS_SUCCEEDED(err)) + err = editor->Init(aDoc, aPresShell, nsIHTMLEditor::eEditorPlaintextMask); + } + else if (mEditorTypeString == "html" || mEditorTypeString == "") // empty string default to HTML editor { - err = editor->Init(aDoc, aPresShell); - if (NS_SUCCEEDED(err) && editor) - { - mEditor = do_QueryInterface(editor); // this does the addref that is the owning reference - mEditorType = eHTMLTextEditorType; - } - } - } - else - { - err = NS_ERROR_INVALID_ARG; // this is not an editor we know about + err = editor->Init(aDoc, aPresShell, 0); + } + else + { + err = NS_ERROR_INVALID_ARG; // this is not an editor we know about #if DEBUG - nsString errorMsg = "Failed to init editor. Unknown editor type \""; - errorMsg += mEditorTypeString; - errorMsg += "\"\n"; - char *errorMsgCString = errorMsg.ToNewCString(); - NS_WARNING(errorMsgCString); - delete [] errorMsgCString; + nsString errorMsg = "Failed to init editor. Unknown editor type \""; + errorMsg += mEditorTypeString; + errorMsg += "\"\n"; + char *errorMsgCString = errorMsg.ToNewCString(); + NS_WARNING(errorMsgCString); + delete [] errorMsgCString; #endif + } + + if (NS_SUCCEEDED(err) && editor) + { + mEditor = do_QueryInterface(editor); // this does the addref that is the owning reference + mEditorType = eHTMLTextEditorType; + } } return err; @@ -441,19 +428,9 @@ nsEditorShell::SetTextProperty(const PRUnichar *prop, const PRUnichar *attr, con switch (mEditorType) { case ePlainTextEditorType: - { // should we allow this? - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->SetTextProperty(styleAtom, &attributeStr, &valueStr); - } - break; case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->SetTextProperty(styleAtom, &attributeStr, &valueStr); - } + err = mEditor->SetInlineProperty(styleAtom, &attributeStr, &valueStr); break; default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -482,19 +459,9 @@ nsEditorShell::RemoveOneProperty(const nsString& aProp, const nsString &aAttr) switch (mEditorType) { case ePlainTextEditorType: - { // should we allow this? - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->RemoveTextProperty(styleAtom, &aAttr); - } - break; case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->RemoveTextProperty(styleAtom, &aAttr); - } + err = mEditor->RemoveInlineProperty(styleAtom, &aAttr); break; default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -566,19 +533,9 @@ nsEditorShell::GetTextProperty(const PRUnichar *prop, const PRUnichar *attr, con switch (mEditorType) { case ePlainTextEditorType: - { // should we allow this? - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->GetTextProperty(styleAtom, &aAttr, &aValue, *firstHas, *anyHas, *allHas); - } - break; case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->GetTextProperty(styleAtom, &aAttr, &aValue, *firstHas, *anyHas, *allHas); - } + err = mEditor->GetInlineProperty(styleAtom, &aAttr, &aValue, *firstHas, *anyHas, *allHas); break; default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -593,25 +550,7 @@ NS_IMETHODIMP nsEditorShell::SetBackgroundColor(const PRUnichar *color) nsAutoString aColor(color); - switch (mEditorType) - { - case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->SetBackgroundColor(aColor); - break; - } - case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - result = textEditor->SetBackgroundColor(aColor); - } - break; - default: - result = NS_ERROR_NOT_IMPLEMENTED; - } + result = mEditor->SetBackgroundColor(aColor); return result; } @@ -622,25 +561,9 @@ NS_IMETHODIMP nsEditorShell::ApplyStyleSheet(const PRUnichar *url) nsAutoString aURL(url); - switch (mEditorType) - { - case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - result = textEditor->ApplyStyleSheet(aURL); - } - break; - case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->ApplyStyleSheet(aURL); - } - break; - default: - result = NS_ERROR_NOT_IMPLEMENTED; - } + nsCOMPtr styleSheetFoobar = do_QueryInterface(mEditor); + if (styleSheetFoobar) + result = styleSheetFoobar->ApplyStyleSheet(aURL); return result; } @@ -656,9 +579,7 @@ NS_IMETHODIMP nsEditorShell::SetBodyAttribute(const PRUnichar *attr, const PRUni { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->SetBodyAttribute(aAttr, aValue); + result = mEditor->SetBodyAttribute(aAttr, aValue); break; } default: @@ -1028,36 +949,31 @@ static NS_DEFINE_IID(kCFileWidgetCID, NS_FILEWIDGET_CID); { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + // This was written for all local file getting + // TODO: NEED TO GET PROPER PARENT WINDOW + PRUnichar *fileURLString = nsnull; + nsAutoString filterType("html"); + result = GetLocalFileURL(nsnull, filterType.GetUnicode(), &fileURLString); + if (NS_FAILED(result) || !fileURLString || !*fileURLString) + return result; - if (htmlEditor) + // all I want to do is call a method on nsToolkitCore that would normally + // be static. But I have to go through all this crap. XPCOM sucks so bad. + static NS_DEFINE_IID(kToolkitCoreCID, NS_TOOLKITCORE_CID); + nsCOMPtr toolkitCore; + result = nsComponentManager::CreateInstance(kToolkitCoreCID, + nsnull, + nsIDOMToolkitCore::GetIID(), + getter_AddRefs(toolkitCore)); + if (NS_SUCCEEDED(result) && toolkitCore) { - // This was written for all local file getting - // TODO: NEED TO GET PROPER PARENT WINDOW - PRUnichar *fileURLString = nsnull; - nsAutoString filterType("html"); - result = GetLocalFileURL(nsnull, filterType.GetUnicode(), &fileURLString); - if (NS_FAILED(result) || !fileURLString || !*fileURLString) - return result; - - // all I want to do is call a method on nsToolkitCore that would normally - // be static. But I have to go through all this crap. XPCOM sucks so bad. - static NS_DEFINE_IID(kToolkitCoreCID, NS_TOOLKITCORE_CID); - nsCOMPtr toolkitCore; - result = nsComponentManager::CreateInstance(kToolkitCoreCID, - nsnull, - nsIDOMToolkitCore::GetIID(), - getter_AddRefs(toolkitCore)); - if (NS_SUCCEEDED(result) && toolkitCore) - { - // at some point we need to be passing nsFileSpecs around. When nsIURI is fileSpec- - // savvy, we should use that. - result = toolkitCore->ShowWindowWithArgs("chrome://editor/content", nsnull, fileURLString/*fileURL.GetAsString()*/); - } - - // delete the string - + // at some point we need to be passing nsFileSpecs around. When nsIURI is fileSpec- + // savvy, we should use that. + result = toolkitCore->ShowWindowWithArgs("chrome://editor/content", nsnull, fileURLString/*fileURL.GetAsString()*/); } + + // delete the string + break; } default: @@ -1076,19 +992,14 @@ nsEditorShell::Save() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Save(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Save(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Save(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1104,19 +1015,14 @@ nsEditorShell::SaveAs() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->SaveAs(PR_FALSE); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->SaveAs(PR_FALSE); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->SaveAs(PR_FALSE); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1243,19 +1149,14 @@ nsEditorShell::Undo() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Undo(1); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Undo(1); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Undo(1); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1271,19 +1172,14 @@ nsEditorShell::Redo() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Redo(1); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Redo(1); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Redo(1); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1299,19 +1195,14 @@ nsEditorShell::Cut() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Cut(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Cut(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Cut(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1327,19 +1218,14 @@ nsEditorShell::Copy() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Copy(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Copy(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Copy(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1355,19 +1241,14 @@ nsEditorShell::Paste() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->Paste(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Paste(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->Paste(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1383,19 +1264,14 @@ nsEditorShell::PasteAsQuotation() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->PasteAsQuotation(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->PasteAsQuotation(); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->PasteAsQuotation(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1413,19 +1289,14 @@ nsEditorShell::PasteAsCitedQuotation(const PRUnichar *cite) switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->PasteAsQuotation(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->PasteAsCitedQuotation(aCiteString); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->PasteAsQuotation(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1443,19 +1314,14 @@ nsEditorShell::InsertAsQuotation(const PRUnichar *quotedText) switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->InsertAsQuotation(aQuotedText); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->InsertAsQuotation(aQuotedText); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->InsertAsQuotation(aQuotedText); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1474,19 +1340,14 @@ nsEditorShell::InsertAsCitedQuotation(const PRUnichar *quotedText, const PRUnich switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->InsertAsQuotation(aQuotedText); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->InsertAsCitedQuotation(aQuotedText, aCiteString); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->InsertAsQuotation(aQuotedText); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1502,19 +1363,14 @@ nsEditorShell::SelectAll() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->SelectAll(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->SelectAll(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->SelectAll(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1527,15 +1383,15 @@ NS_IMETHODIMP nsEditorShell::DeleteSelection(PRInt32 action) { nsresult err = NS_NOINTERFACE; - nsIEditor::ECollapsedSelectionAction selectionAction; + nsIEditor::ESelectionCollapseDirection selectionAction; switch(action) { - case nsIEditor::eDeleteRight: - selectionAction = nsIEditor::eDeleteRight; + case 1: + selectionAction = nsIEditor::eDeleteNext; break; - case nsIEditor::eDeleteLeft: - selectionAction = nsIEditor::eDeleteLeft; + case 2: + selectionAction = nsIEditor::eDeletePrevious; break; default: selectionAction = nsIEditor::eDoNothing; @@ -1560,12 +1416,6 @@ nsEditorShell::InsertText(const PRUnichar *textToInsert) switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->InsertText(aTextToInsert); - } - break; case eHTMLTextEditorType: { nsCOMPtr htmlEditor = do_QueryInterface(mEditor); @@ -1573,6 +1423,7 @@ nsEditorShell::InsertText(const PRUnichar *textToInsert) err = htmlEditor->InsertText(aTextToInsert); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1590,12 +1441,6 @@ nsEditorShell::InsertSource(const PRUnichar *aSourceToInsert) switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->InsertText(sourceToInsert); - } - break; case eHTMLTextEditorType: { nsCOMPtr htmlEditor = do_QueryInterface(mEditor); @@ -1603,6 +1448,7 @@ nsEditorShell::InsertSource(const PRUnichar *aSourceToInsert) err = htmlEditor->InsertHTML(sourceToInsert); } break; + default: err = NS_NOINTERFACE; } @@ -1615,9 +1461,8 @@ nsEditorShell::InsertBreak() { nsresult err = NS_NOINTERFACE; - nsCOMPtr editor = do_QueryInterface(mEditor); - if (editor) - err = editor->InsertBreak(); + if (mEditor) + err = mEditor->InsertBreak(); return err; } @@ -1684,16 +1529,10 @@ nsEditorShell::GetContentsAs(const PRUnichar *format, PRUint32 flags, nsString aFormat (format); nsString aContentsAs; - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->OutputToString(aContentsAs, aFormat, flags); - else - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->OutputToString(aContentsAs, aFormat, flags); - } - + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->OutputToString(aContentsAs, aFormat, flags); + *contentsAs = aContentsAs.ToNewUnicode(); return err; @@ -1728,11 +1567,11 @@ nsEditorShell::GetWrapColumn(PRInt32* aWrapColumn) { case ePlainTextEditorType: { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) { PRInt32 wc; - err = textEditor->GetBodyWrapWidth(&wc); + err = mailEditor->GetBodyWrapWidth(&wc); if (NS_SUCCEEDED(err)) *aWrapColumn = (PRInt32)wc; } @@ -1758,9 +1597,9 @@ nsEditorShell::SetWrapColumn(PRInt32 aWrapColumn) { case ePlainTextEditorType: { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->SetBodyWrapWidth(mWrapColumn); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + err = mailEditor->SetBodyWrapWidth(mWrapColumn); } break; default: @@ -1781,11 +1620,7 @@ nsEditorShell::GetParagraphFormat(PRUnichar * *paragraphFormat) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->GetParagraphFormat(aParagraphFormat); - } + err = mEditor->GetParagraphFormat(aParagraphFormat); break; default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -1806,12 +1641,9 @@ nsEditorShell::SetParagraphFormat(PRUnichar * paragraphFormat) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->SetParagraphFormat(aParagraphFormat); - } + err = mEditor->SetParagraphFormat(aParagraphFormat); break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -1871,12 +1703,9 @@ nsEditorShell::InsertList(const PRUnichar *listType) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->InsertList(aListType); - } + err = mEditor->InsertList(aListType); break; + case ePlainTextEditorType: default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -1895,12 +1724,9 @@ nsEditorShell::Indent(const PRUnichar *indent) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Indent(aIndent); - } + err = mEditor->Indent(aIndent); break; + case ePlainTextEditorType: default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -1919,12 +1745,9 @@ nsEditorShell::Align(const PRUnichar *align) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->Align(aAlignType); - } + err = mEditor->Align(aAlignType); break; + case ePlainTextEditorType: default: err = NS_ERROR_NOT_IMPLEMENTED; @@ -1934,24 +1757,20 @@ nsEditorShell::Align(const PRUnichar *align) } NS_IMETHODIMP -nsEditorShell::GetSelectedElement(const PRUnichar *tagName, nsIDOMElement **_retval) +nsEditorShell::GetSelectedElement(const PRUnichar *aInTagName, nsIDOMElement **aOutElement) { - if (!tagName || !_retval) + if (!aInTagName || !aOutElement) return NS_ERROR_NULL_POINTER; nsresult result = NS_NOINTERFACE; - nsAutoString TagName(tagName); + nsAutoString tagName(aInTagName); switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - nsCOMPtr element; - if (htmlEditor) - return htmlEditor->GetSelectedElement(TagName, _retval); - } + result = mEditor->GetSelectedElement(tagName, aOutElement); break; + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; @@ -1961,24 +1780,22 @@ nsEditorShell::GetSelectedElement(const PRUnichar *tagName, nsIDOMElement **_ret } NS_IMETHODIMP -nsEditorShell::GetElementOrParentByTagName(const PRUnichar *tagName, nsIDOMNode *node, nsIDOMElement **_retval) +nsEditorShell::GetElementOrParentByTagName(const PRUnichar *aInTagName, nsIDOMNode *node, nsIDOMElement **aOutElement) { //node can be null -- this signals using the selection anchorNode - if (!tagName || !_retval) + if (!aInTagName || !aOutElement) return NS_ERROR_NULL_POINTER; nsresult result = NS_NOINTERFACE; - nsAutoString TagName(tagName); + nsAutoString tagName(aInTagName); switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - return htmlEditor->GetElementOrParentByTagName(TagName, node, _retval); - } + result = mEditor->GetElementOrParentByTagName(tagName, node, aOutElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -1987,23 +1804,20 @@ nsEditorShell::GetElementOrParentByTagName(const PRUnichar *tagName, nsIDOMNode } NS_IMETHODIMP -nsEditorShell::CreateElementWithDefaults(const PRUnichar *tagName, nsIDOMElement **_retval) +nsEditorShell::CreateElementWithDefaults(const PRUnichar *aInTagName, nsIDOMElement **aOutElement) { - if (!_retval) + if (!aOutElement) return NS_ERROR_NULL_POINTER; nsresult result = NS_NOINTERFACE; - nsAutoString aTagName(tagName); + nsAutoString tagName(aInTagName); switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - return htmlEditor->CreateElementWithDefaults(aTagName, _retval); - } + result = mEditor->CreateElementWithDefaults(tagName, aOutElement); break; + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; @@ -2023,12 +1837,10 @@ nsEditorShell::InsertElement(nsIDOMElement *element, PRBool deleteSelection) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->InsertElement(element, deleteSelection); - } + result = mEditor->InsertElement(element, deleteSelection); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2043,12 +1855,11 @@ nsEditorShell::SaveHLineSettings(nsIDOMElement* aElement) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - return htmlEditor->SaveHLineSettings(aElement); - } + // this is bogus. We should save the HLine settings (or HRule, as it's more properly known) here. + result = mEditor->SaveHLineSettings(aElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2062,12 +1873,10 @@ nsEditorShell::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - return htmlEditor->InsertLinkAroundSelection(aAnchorElement); - } + result = mEditor->InsertLinkAroundSelection(aAnchorElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2084,12 +1893,10 @@ nsEditorShell::SelectElement(nsIDOMElement* aElement) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->SelectElement(aElement); - } + result = mEditor->SelectElement(aElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2107,12 +1914,10 @@ nsEditorShell::SetSelectionAfterElement(nsIDOMElement* aElement) switch (mEditorType) { case eHTMLTextEditorType: - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->SetCaretAfterElement(aElement); - } + result = mEditor->SetCaretAfterElement(aElement); break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2130,11 +1935,13 @@ nsEditorShell::InsertTableCell(PRInt32 aNumber, PRBool bAfter) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->InsertTableCell(aNumber, bAfter); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->InsertTableCell(aNumber, bAfter); } break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2149,11 +1956,13 @@ nsEditorShell::InsertTableRow(PRInt32 aNumber, PRBool bAfter) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->InsertTableRow(aNumber,bAfter); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->InsertTableRow(aNumber,bAfter); } break; + + case ePlainTextEditorType: default: result = NS_ERROR_NOT_IMPLEMENTED; } @@ -2168,9 +1977,9 @@ nsEditorShell::InsertTableColumn(PRInt32 aNumber, PRBool bAfter) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->InsertTableColumn(aNumber,bAfter); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->InsertTableColumn(aNumber,bAfter); } break; default: @@ -2187,9 +1996,9 @@ nsEditorShell::DeleteTable() { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->DeleteTable(); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->DeleteTable(); } break; default: @@ -2206,9 +2015,9 @@ nsEditorShell::DeleteTableCell(PRInt32 aNumber) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->DeleteTableCell(aNumber); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->DeleteTableCell(aNumber); } break; default: @@ -2226,9 +2035,9 @@ nsEditorShell::DeleteTableRow(PRInt32 aNumber) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->DeleteTableRow(aNumber); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->DeleteTableRow(aNumber); } break; default: @@ -2246,9 +2055,9 @@ nsEditorShell::DeleteTableColumn(PRInt32 aNumber) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->DeleteTableColumn(aNumber); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->DeleteTableColumn(aNumber); } break; default: @@ -2265,9 +2074,9 @@ nsEditorShell::JoinTableCells() { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->JoinTableCells(); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->JoinTableCells(); } break; default: @@ -2284,9 +2093,9 @@ nsEditorShell::NormalizeTable(nsIDOMElement *aTable) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->NormalizeTable(aTable); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->NormalizeTable(aTable); } break; default: @@ -2310,12 +2119,12 @@ nsEditorShell::GetRowIndex(nsIDOMElement *cellElement, PRInt32 *_retval) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) { // Get both row and column indexes - return just row PRInt32 colIndex; - result = htmlEditor->GetCellIndexes(cellElement, *_retval, colIndex); + result = tableEditor->GetCellIndexes(cellElement, *_retval, colIndex); } } break; @@ -2336,12 +2145,12 @@ nsEditorShell::GetColumnIndex(nsIDOMElement *cellElement, PRInt32 *_retval) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) { // Get both row and column indexes - return just column PRInt32 rowIndex; - result = htmlEditor->GetCellIndexes(cellElement, rowIndex, *_retval); + result = tableEditor->GetCellIndexes(cellElement, rowIndex, *_retval); } } break; @@ -2363,12 +2172,12 @@ nsEditorShell::GetTableRowCount(nsIDOMElement *tableElement, PRInt32 *_retval) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) { // This returns both the number of rows and columns: return just rows PRInt32 cols; - result = htmlEditor->GetTableSize(tableElement, *_retval, cols); + result = tableEditor->GetTableSize(tableElement, *_retval, cols); } } break; @@ -2390,12 +2199,12 @@ nsEditorShell::GetTableColumnCount(nsIDOMElement *tableElement, PRInt32 *_retval { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) { // This returns both the number of rows and columns: return just columns PRInt32 rows; - result = htmlEditor->GetTableSize(tableElement, rows, *_retval); + result = tableEditor->GetTableSize(tableElement, rows, *_retval); } } break; @@ -2417,9 +2226,9 @@ nsEditorShell::GetCellAt(nsIDOMElement *tableElement, PRInt32 rowIndex, PRInt32 { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->GetCellAt(tableElement, rowIndex, colIndex, *_retval); + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetCellAt(tableElement, rowIndex, colIndex, *_retval); } break; default: @@ -2446,9 +2255,9 @@ nsEditorShell::GetCellDataAt(nsIDOMElement *tableElement, PRInt32 rowIndex, PRIn { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->GetCellDataAt(tableElement, rowIndex, colIndex, *_retval, + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetCellDataAt(tableElement, rowIndex, colIndex, *_retval, *aStartRowIndex, *aStartColIndex, *aRowSpan, *aColSpan, *aIsSelected); } break; @@ -2473,9 +2282,9 @@ nsEditorShell::GetEmbeddedObjects(nsISupportsArray **aObjectArray) { case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - result = htmlEditor->GetEmbeddedObjects(aObjectArray); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + result = mailEditor->GetEmbeddedObjects(aObjectArray); } break; @@ -2714,19 +2523,14 @@ nsEditorShell::BeginBatchChanges() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->BeginTransaction(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->BeginTransaction(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->BeginTransaction(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -2742,19 +2546,14 @@ nsEditorShell::EndBatchChanges() switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->EndTransaction(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->EndTransaction(); + nsCOMPtr editor = do_QueryInterface(mEditor); + if (editor) + err = editor->EndTransaction(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } @@ -2822,30 +2621,21 @@ nsEditorShell::StartLogging(nsIFileSpec *logFile) { nsresult err = NS_OK; -#if 1 - switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->StartLogging(logFile); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->StartLogging(logFile); + nsCOMPtr logger = do_QueryInterface(mEditor); + if (logger) + err = logger->StartLogging(logFile); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } -#endif - return err; } @@ -2854,30 +2644,21 @@ nsEditorShell::StopLogging() { nsresult err = NS_OK; -#if 1 - switch (mEditorType) { case ePlainTextEditorType: - { - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - err = textEditor->StopLogging(); - } - break; case eHTMLTextEditorType: { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - err = htmlEditor->StopLogging(); + nsCOMPtr logger = do_QueryInterface(mEditor); + if (logger) + err = logger->StopLogging(); } break; + default: err = NS_ERROR_NOT_IMPLEMENTED; } -#endif - return err; } diff --git a/editor/composer/src/nsEditorShell.h b/editor/composer/src/nsEditorShell.h index 13bc408c1f68..42c87834742a 100644 --- a/editor/composer/src/nsEditorShell.h +++ b/editor/composer/src/nsEditorShell.h @@ -15,8 +15,8 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ -#ifndef nsEditorAppCore_h___ -#define nsEditorAppCore_h___ +#ifndef nsEditorShell_h___ +#define nsEditorShell_h___ //#include "nsAppCores.h" @@ -40,6 +40,7 @@ #include "nsIEditorSpellCheck.h" #include "nsISpellChecker.h" #include "nsInterfaceState.h" +#include "nsIHTMLEditor.h" class nsIBrowserWindow; class nsIWebShell; @@ -50,13 +51,11 @@ class nsIDOMNode; class nsIURI; class nsIWebShellWindow; class nsIPresShell; -class nsIHTMLEditor; -class nsITextEditor; class nsIOutputStream; class nsISupportsArray; -#define NS_EDITORAPPCORE_CID \ +#define NS_EDITORSHELL_CID \ { /* {} */ \ 0x9afff72b, 0xca9a, 0x11d2, \ { 0x96, 0xc9, 0x0, 0x60, 0xb0, 0xfb, 0x99, 0x56 } \ @@ -316,7 +315,7 @@ class nsEditorShell : public nsIEditorShell, EEditorType mEditorType; nsString mEditorTypeString; // string which describes which editor type will be instantiated (lowercased) - nsCOMPtr mEditor; // this can be either an HTML or plain text (or other?) editor + nsCOMPtr mEditor; // this can be either an HTML or plain text (or other?) editor nsCOMPtr mSearchContext; // context used for search and replace. Owned by the appshell. @@ -336,4 +335,4 @@ class nsEditorShell : public nsIEditorShell, nsCOMPtr mDocStateListeners; // contents are nsISupports }; -#endif // nsEditorAppCore_h___ +#endif // nsEditorShell_h___ diff --git a/editor/composer/src/nsInterfaceState.cpp b/editor/composer/src/nsInterfaceState.cpp index 7af4a39a8e91..09a4f5ed43bd 100644 --- a/editor/composer/src/nsInterfaceState.cpp +++ b/editor/composer/src/nsInterfaceState.cpp @@ -30,7 +30,7 @@ #include "nsIDOMElement.h" #include "nsIDOMSelection.h" -#include "nsITextEditor.h" +#include "nsIEditor.h" #include "nsIHTMLEditor.h" #include "nsInterfaceState.h" @@ -88,7 +88,7 @@ nsInterfaceState::QueryInterface(const nsIID& aIID, void** aInstancePtr) } NS_IMETHODIMP -nsInterfaceState::Init(nsISupports* aEditor, nsIWebShell *aChromeWebShell) +nsInterfaceState::Init(nsIHTMLEditor* aEditor, nsIWebShell *aChromeWebShell) { if (!aEditor) return NS_ERROR_INVALID_ARG; @@ -153,9 +153,7 @@ nsInterfaceState::UpdateParagraphState(const char* observerName, const char* att { nsStringArray tagList; - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - htmlEditor->GetParagraphStyle(&tagList); + mEditor->GetParagraphStyle(&tagList); PRInt32 numTags = tagList.Count(); if (numTags > 0) @@ -184,18 +182,14 @@ nsInterfaceState::UpdateListState(const char* observerName, const char* tagName) editor->GetSelection(getter_AddRefs(domSelection)); } - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tagStr(tagName); - - nsCOMPtr domNode; - if (domSelection) - domSelection->GetAnchorNode(getter_AddRefs(domNode)); - - nsCOMPtr parentElement; - rv = htmlEditor->GetElementOrParentByTagName(tagStr, domNode, getter_AddRefs(parentElement)); - } + nsAutoString tagStr(tagName); + + nsCOMPtr domNode; + if (domSelection) + domSelection->GetAnchorNode(getter_AddRefs(domNode)); + + nsCOMPtr parentElement; + rv = mEditor->GetElementOrParentByTagName(tagStr, domNode, getter_AddRefs(parentElement)); return rv; } @@ -212,18 +206,7 @@ nsInterfaceState::UpdateFontFace(const char* observerName, const char* attribute nsCOMPtr styleAtom = getter_AddRefs(NS_NewAtom("font")); nsAutoString faceStr("face"); - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - { - rv = textEditor->GetTextProperty(styleAtom, &faceStr, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); - } - else - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - rv = htmlEditor->GetTextProperty(styleAtom, &faceStr, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); - } - + rv = mEditor->GetInlineProperty(styleAtom, &faceStr, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); return rv; } @@ -240,17 +223,7 @@ nsInterfaceState::UpdateTextState(const char* tagName, const char* observerName, nsCOMPtr styleAtom = getter_AddRefs(NS_NewAtom(tagName)); - nsCOMPtr textEditor = do_QueryInterface(mEditor); - if (textEditor) - { - rv = textEditor->GetTextProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); - } - else - { - nsCOMPtr htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - rv = htmlEditor->GetTextProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); - } + rv = mEditor->GetInlineProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp); PRBool &behaviour = allOfSelectionHasProp; // change this to alter the behaviour if (behaviour != ioState) @@ -309,7 +282,7 @@ nsInterfaceState::SetNodeAttribute(const char* nodeID, const char* attributeName } -nsresult NS_NewInterfaceState(nsISupports* aEditor, nsIWebShell* aWebShell, nsIDOMSelectionListener** aInstancePtrResult) +nsresult NS_NewInterfaceState(nsIHTMLEditor* aEditor, nsIWebShell* aWebShell, nsIDOMSelectionListener** aInstancePtrResult) { nsInterfaceState* newThang = new nsInterfaceState; if (!newThang) diff --git a/editor/composer/src/nsInterfaceState.h b/editor/composer/src/nsInterfaceState.h index 3203ad4c066a..abab7019140d 100644 --- a/editor/composer/src/nsInterfaceState.h +++ b/editor/composer/src/nsInterfaceState.h @@ -25,6 +25,8 @@ #include "nsIDocumentStateListener.h" #include "nsIWebShell.h" +class nsIHTMLEditor; + // class responsible for communicating changes in local state back to the UI. // This is currently somewhat tied to a given XUL UI implementation @@ -38,7 +40,7 @@ public: NS_DECL_ISUPPORTS - NS_IMETHOD Init(nsISupports* aEditor, nsIWebShell *aChromeWebShell); + NS_IMETHOD Init(nsIHTMLEditor* aEditor, nsIWebShell *aChromeWebShell); // nsIDOMSelectionListener interface NS_IMETHOD NotifySelectionChanged(); @@ -67,7 +69,7 @@ protected: // this class should not hold references to the editor or editorShell. Doing // so would result in cirular reference chains. - nsISupports* mEditor; // the text or HTML editor + nsIHTMLEditor* mEditor; // the HTML editor nsIWebShell* mWebShell; // web shell for the chrome area // current state @@ -83,6 +85,6 @@ protected: }; -extern "C" nsresult NS_NewInterfaceState(nsISupports* aEditor, nsIWebShell* aWebShell, nsIDOMSelectionListener** aInstancePtrResult); +extern "C" nsresult NS_NewInterfaceState(nsIHTMLEditor* aEditor, nsIWebShell* aWebShell, nsIDOMSelectionListener** aInstancePtrResult); #endif // nsInterfaceState_h__ diff --git a/editor/libeditor/base/CreateElementTxn.cpp b/editor/libeditor/base/CreateElementTxn.cpp index fa7156334fda..930a59ec7599 100644 --- a/editor/libeditor/base/CreateElementTxn.cpp +++ b/editor/libeditor/base/CreateElementTxn.cpp @@ -79,7 +79,7 @@ NS_IMETHODIMP CreateElementTxn::Do(void) result = mEditor->GetDocument(getter_AddRefs(doc)); if ((NS_SUCCEEDED(result)) && (doc)) { - if (nsIEditor::GetTextNodeTag() == mTag) + if (nsEditor::GetTextNodeTag() == mTag) { const nsString stringData; nsCOMPtrnewTextNode; diff --git a/editor/libeditor/base/DeleteRangeTxn.cpp b/editor/libeditor/base/DeleteRangeTxn.cpp index 33319eed5f74..79860e0a795d 100644 --- a/editor/libeditor/base/DeleteRangeTxn.cpp +++ b/editor/libeditor/base/DeleteRangeTxn.cpp @@ -123,7 +123,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) else { // the selection ends in a different node from where it started // delete the relevant content in the start node - result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eDeleteRight); + result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eDeleteNext); if (NS_SUCCEEDED(result)) { // delete the intervening nodes @@ -131,7 +131,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) if (NS_SUCCEEDED(result)) { // delete the relevant content in the end node - result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eDeleteLeft); + result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eDeletePrevious); } } } @@ -281,7 +281,7 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, PRUint32 aOffset, - nsIEditor::ECollapsedSelectionAction aAction) + nsIEditor::ESelectionCollapseDirection aAction) { nsresult result; // see what kind of node we have @@ -290,7 +290,7 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, if (textNode) { // if the node is a text node, then delete text content PRUint32 start, numToDelete; - if (nsIEditor::eDeleteRight == aAction) + if (nsIEditor::eDeleteNext == aAction) { start=aOffset; textNode->GetLength(&numToDelete); diff --git a/editor/libeditor/base/DeleteRangeTxn.h b/editor/libeditor/base/DeleteRangeTxn.h index 9df23001fe7e..0d1fe5af79f7 100644 --- a/editor/libeditor/base/DeleteRangeTxn.h +++ b/editor/libeditor/base/DeleteRangeTxn.h @@ -32,6 +32,9 @@ class nsIDOMDocument; 0x5ec6b260, 0xac49, 0x11d2, \ {0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } +class nsIDOMRange; +class nsIEditor; + /** * A transaction that deletes an entire range in the content tree */ @@ -78,7 +81,7 @@ protected: NS_IMETHOD CreateTxnsToDeleteContent(nsIDOMNode *aParent, PRUint32 aOffset, - nsIEditor::ECollapsedSelectionAction aAction); + nsIEditor::ESelectionCollapseDirection aAction); protected: diff --git a/editor/libeditor/base/nsEditRules.h b/editor/libeditor/base/nsEditRules.h index 5152505ff5d5..8b32009b3385 100644 --- a/editor/libeditor/base/nsEditRules.h +++ b/editor/libeditor/base/nsEditRules.h @@ -19,7 +19,7 @@ #ifndef nsEditRules_h__ #define nsEditRules_h__ -class nsIEditor; +class nsHTMLEditor; class nsIDOMSelection; /*************************************************************************** @@ -43,9 +43,11 @@ class nsRulesInfo class nsEditRules { public: - NS_IMETHOD Init(nsIEditor *aEditor)=0; + NS_IMETHOD Init(nsHTMLEditor *aEditor)=0; NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel)=0; NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult)=0; + NS_IMETHOD GetFlags(PRUint32 *aFlags)=0; + NS_IMETHOD SetFlags(PRUint32 aFlags)=0; }; diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 6497e9026f9d..79bd3e35aa9e 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -16,12 +16,14 @@ * Reserved. */ +#include "nsVector.h" +#include "nsVoidArray.h" #include "nsIDOMDocument.h" #include "nsIPref.h" #include "nsILocale.h" -#include "nsEditor.h" #include "nsIEditProperty.h" // to be removed XXX + #include "nsIDOMText.h" #include "nsIDOMElement.h" #include "nsIDOMAttr.h" @@ -31,12 +33,7 @@ #include "nsIDOMRange.h" #include "nsIDocument.h" #include "nsIDiskDocument.h" -#include "nsVector.h" #include "nsIServiceManager.h" -#include "nsEditFactory.h" -#include "nsTextEditFactory.h" -#include "nsHTMLEditFactory.h" -#include "nsEditorCID.h" #include "nsTransactionManagerCID.h" #include "nsITransactionManager.h" #include "nsIPresShell.h" @@ -45,7 +42,6 @@ #include "nsIDOMSelection.h" #include "nsIEnumerator.h" #include "nsIAtom.h" -#include "nsVoidArray.h" #include "nsISupportsArray.h" #include "nsICaret.h" #include "nsIStyleContext.h" @@ -55,9 +51,9 @@ #include "nsICSSStyleSheet.h" #include "nsIHTMLContentContainer.h" #include "nsIStyleSet.h" -#include "nsStyleSheetTxns.h" #include "nsIDocumentObserver.h" #include "nsIDocumentStateListener.h" +#include "nsIStringStream.h" #ifdef NECKO #include "nsNeckoUtil.h" @@ -65,9 +61,6 @@ #include "nsIURL.h" #endif // NECKO -#include "nsEditorShell.h" -#include "nsEditorShellFactory.h" - #include "nsIContent.h" #include "nsIContentIterator.h" #include "nsLayoutCID.h" @@ -89,12 +82,15 @@ #include "DeleteRangeTxn.h" #include "SplitElementTxn.h" #include "JoinElementTxn.h" -#include "nsIStringStream.h" +#include "nsStyleSheetTxns.h" #include "IMETextTxn.h" #include "IMECommitTxn.h" // #define HACK_FORCE_REDRAW 1 +#include "nsEditorCID.h" +#include "nsEditor.h" + #ifdef HACK_FORCE_REDRAW // INCLUDES FOR EVIL HACK TO FOR REDRAW @@ -106,32 +102,19 @@ // Drag & Drop, Clipboard #include "nsWidgetsCID.h" +#include "nsIFileWidget.h" #include "nsIClipboard.h" #include "nsITransferable.h" -// Drag & Drop, Clipboard Support -static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); -static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); - static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); -static NS_DEFINE_CID(kEditorCID, NS_EDITOR_CID); -static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); -static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); -static NS_DEFINE_IID(kEditorShellCID, NS_EDITORAPPCORE_CID); static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); // transaction manager static NS_DEFINE_CID(kCTransactionManagerCID, NS_TRANSACTIONMANAGER_CID); -static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID); -static NS_DEFINE_CID(kCDOMRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); static NS_DEFINE_CID(kPrefCID, NS_PREF_CID); -// factory classes -static NS_DEFINE_IID(kIEditFactoryIID, NS_IEDITORFACTORY_IID); -static NS_DEFINE_IID(kIHTMLEditFactoryIID, NS_IHTMLEDITORFACTORY_IID); -static NS_DEFINE_IID(kITextEditFactoryIID, NS_ITEXTEDITORFACTORY_IID); #ifdef XP_PC #define TRANSACTION_MANAGER_DLL "txmgr.dll" @@ -159,159 +142,8 @@ static const PRBool gNoisy = PR_FALSE; -/* ----- TEST METHODS DECLARATIONS ----- */ -// Methods defined here are TEMPORARY -//NS_IMETHODIMP GetColIndexForCell(nsIPresShell *aPresShell, nsIDOMNode *aCellNode, PRInt32 &aCellIndex); -/* ----- END TEST METHOD DECLARATIONS ----- */ - - PRInt32 nsEditor::gInstanceCount = 0; -//monitor for the editor - - - -PRMonitor *GetEditorMonitor() //if more than one person asks for the monitor at the same time for the FIRST time, we are screwed -{ - static PRMonitor *ns_editlock = nsnull; - if (nsnull == ns_editlock) - { - ns_editlock = (PRMonitor *)1; //how long will the next line take? lets cut down on the chance of reentrancy - ns_editlock = PR_NewMonitor(); - } - else if ((PRMonitor *)1 == ns_editlock) - return GetEditorMonitor(); - return ns_editlock; -} - -nsIComponentManager* gCompMgr = NULL; - -/* -we must be good providers of factories etc. this is where to put ALL editor exports -*/ -//BEGIN EXPORTS -extern "C" NS_EXPORT nsresult NSGetFactory(nsISupports * aServMgr, - const nsCID & aClass, - const char * aClassName, - const char * aProgID, - nsIFactory ** aFactory) -{ - if (nsnull == aFactory) { - return NS_ERROR_NULL_POINTER; - } - - *aFactory = nsnull; - - nsresult rv; - nsCOMPtr servMgr(do_QueryInterface(aServMgr, &rv)); - if (NS_FAILED(rv)) return rv; - - rv = servMgr->GetService(kComponentManagerCID, nsIComponentManager::GetIID(), - (nsISupports**)&gCompMgr); - if (NS_FAILED(rv)) return rv; - - rv = NS_NOINTERFACE; - - if (aClass.Equals(kEditorCID)) { - rv = GetEditFactory(aFactory, aClass); - if (NS_FAILED(rv)) goto done; - } - else if (aClass.Equals(kTextEditorCID)) { - rv = GetTextEditFactory(aFactory, aClass); - if (NS_FAILED(rv)) goto done; - } - else if (aClass.Equals(kHTMLEditorCID)) { - rv = GetHTMLEditFactory(aFactory, aClass); - if (NS_FAILED(rv)) goto done; - } - else if (aClass.Equals(kEditorShellCID)) { - rv = GetEditorShellFactory(aFactory, aClass, aClassName, aProgID); - if (NS_FAILED(rv)) goto done; - } - - done: - (void)servMgr->ReleaseService(kComponentManagerCID, gCompMgr); - - return rv; -} - - - -extern "C" NS_EXPORT PRBool -NSCanUnload(nsISupports* aServMgr) -{ - return nsEditor::gInstanceCount; //I have no idea. I am copying code here -} - - - -extern "C" NS_EXPORT nsresult -NSRegisterSelf(nsISupports* aServMgr, const char *path) -{ - nsresult rv; - nsCOMPtr servMgr(do_QueryInterface(aServMgr, &rv)); - if (NS_FAILED(rv)) return rv; - - nsIComponentManager* compMgr; - rv = servMgr->GetService(kComponentManagerCID, - nsIComponentManager::GetIID(), - (nsISupports**)&compMgr); - if (NS_FAILED(rv)) return rv; - - rv = compMgr->RegisterComponent(kEditorCID, NULL, NULL, path, - PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) goto done; - rv = compMgr->RegisterComponent(kTextEditorCID, NULL, NULL, path, - PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) goto done; - rv = compMgr->RegisterComponent(kHTMLEditorCID, NULL, NULL, path, - PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) goto done; - rv = compMgr->RegisterComponent(kEditorShellCID, - "Editor Shell Component", - "component://netscape/editor/editorshell", - path, PR_TRUE, PR_TRUE); - - if (NS_FAILED(rv)) goto done; - rv = compMgr->RegisterComponent(kEditorShellCID, - "Editor Shell Spell Checker", - "component://netscape/editor/editorspellcheck", - path, PR_TRUE, PR_TRUE); - - done: - (void)servMgr->ReleaseService(kComponentManagerCID, compMgr); - return rv; -} - -extern "C" NS_EXPORT nsresult -NSUnregisterSelf(nsISupports* aServMgr, const char *path) -{ - nsresult rv; - - nsCOMPtr servMgr(do_QueryInterface(aServMgr, &rv)); - if (NS_FAILED(rv)) return rv; - - nsIComponentManager* compMgr; - rv = servMgr->GetService(kComponentManagerCID, - nsIComponentManager::GetIID(), - (nsISupports**)&compMgr); - if (NS_FAILED(rv)) return rv; - - rv = compMgr->UnregisterComponent(kEditorCID, path); - if (NS_FAILED(rv)) goto done; - rv = compMgr->UnregisterComponent(kTextEditorCID, path); - if (NS_FAILED(rv)) goto done; - rv = compMgr->UnregisterComponent(kHTMLEditorCID, path); - if (NS_FAILED(rv)) goto done; - rv = compMgr->UnregisterComponent(kEditorShellCID, path); - - done: - (void)servMgr->ReleaseService(kComponentManagerCID, compMgr); - return rv; -} - -//END EXPORTS - //class implementations are in order they are declared in nsEditor.h @@ -333,11 +165,8 @@ nsEditor::nsEditor() PR_EnterMonitor(GetEditorMonitor()); gInstanceCount++; PR_ExitMonitor(GetEditorMonitor()); - } - - nsEditor::~nsEditor() { // not sure if this needs to be called earlier. @@ -371,22 +200,19 @@ nsEditor::~nsEditor() } - // BEGIN nsEditor core implementation - NS_IMPL_ADDREF(nsEditor) - NS_IMPL_RELEASE(nsEditor) - NS_IMETHODIMP nsEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) { - if (nsnull == aInstancePtr) { + if (!aInstancePtr) return NS_ERROR_NULL_POINTER; - } + + *aInstancePtr = nsnull; if (aIID.Equals(nsCOMTypeInfo::GetIID())) { nsIEditor *tmp = this; nsISupports *tmp2 = tmp; @@ -399,103 +225,34 @@ nsEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) NS_ADDREF_THIS(); return NS_OK; } + if (aIID.Equals(nsIEditorIMESupport::GetIID())) { + *aInstancePtr = (void*)(nsIEditorIMESupport*)this; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(nsIEditorLogging::GetIID())) { + *aInstancePtr = (void*)(nsIEditorLogging*)this; + NS_ADDREF_THIS(); + return NS_OK; + } + return NS_NOINTERFACE; } -NS_IMETHODIMP -nsEditor::GetDocument(nsIDOMDocument **aDoc) -{ - if (!aDoc) - return NS_ERROR_NULL_POINTER; - *aDoc = nsnull; // init out param - NS_PRECONDITION(mDoc, "bad state, null mDoc"); - if (!mDoc) - return NS_ERROR_NOT_INITIALIZED; - return mDoc->QueryInterface(nsIDOMDocument::GetIID(), (void **)aDoc); -} - -// This seems like too much work! There should be a "nsDOMDocument::GetBody()" -NS_IMETHODIMP -nsEditor::GetBodyElement(nsIDOMElement **aBodyElement) -{ - nsresult result; - - if (!aBodyElement) - return NS_ERROR_NULL_POINTER; - - *aBodyElement = 0; - - NS_PRECONDITION(mDoc, "bad state, null mDoc"); - if (!mDoc) - return NS_ERROR_NOT_INITIALIZED; - - nsCOMPtrnodeList; - nsString bodyTag = "body"; - - result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - - if (NS_FAILED(result)) - return result; - - if (!nodeList) - return NS_ERROR_NULL_POINTER; - - PRUint32 count; - nodeList->GetLength(&count); - - NS_ASSERTION(1==count, "More than one body found in document!"); - - if (count < 1) - return NS_ERROR_FAILURE; - - // Use the first body node in the list: - nsCOMPtr node; - result = nodeList->Item(0, getter_AddRefs(node)); - if (NS_SUCCEEDED(result) && node) - { - //return node->QueryInterface(nsIDOMElement::GetIID(), (void **)aBodyElement); - // Is above equivalent to this: - nsCOMPtr bodyElement = do_QueryInterface(node); - if (bodyElement) - { - *aBodyElement = bodyElement; - // A "getter" method should always addref - NS_ADDREF(*aBodyElement); - } - } - return result; -} - -nsresult -nsEditor::GetPresShell(nsIPresShell **aPS) -{ - if (!aPS) - return NS_ERROR_NULL_POINTER; - *aPS = nsnull; // init out param - NS_PRECONDITION(mPresShell, "bad state, null mPresShell"); - if (!mPresShell) - return NS_ERROR_NOT_INITIALIZED; - return mPresShell->QueryInterface(nsIPresShell::GetIID(), (void **)aPS); -} - +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorMethods --- +#pragma mark - +#endif NS_IMETHODIMP -nsEditor::GetSelection(nsIDOMSelection **aSelection) -{ - if (!aSelection) - return NS_ERROR_NULL_POINTER; - *aSelection = nsnull; - nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, aSelection); // does an addref - return result; -} - -NS_IMETHODIMP -nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell) +nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, PRUint32 aFlags) { NS_PRECONDITION(nsnull!=aDoc && nsnull!=aPresShell, "bad arg"); if ((nsnull==aDoc) || (nsnull==aPresShell)) return NS_ERROR_NULL_POINTER; + mFlags = aFlags; mDoc = aDoc; mPresShell = aPresShell; // we don't addref the pres shell @@ -559,29 +316,9 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell) if (NS_SUCCEEDED(result) && service) { -#if 1 nsILocale* locale = nsnull; result = service->CreateBundle(EDITOR_BUNDLE_URL, locale, getter_AddRefs(mStringBundle)); -#else - nsCOMPtr url; -#ifndef NECKO - result = NS_NewURL(getter_AddRefs(url), nsString(EDITOR_BUNDLE_URL)); -#else - result = NS_NewURI(getter_AddRefs(url), nsString(EDITOR_BUNDLE_URL)); -#endif // NECKO - - if (NS_SUCCEEDED(result) && url) - { - nsILocale* locale = nsnull; - result = service->CreateBundle(url, locale, getter_AddRefs(mStringBundle)); - if (NS_FAILED(result)) - printf("ERROR: Failed to get Create StringBundle\n"); - - } else { - printf("ERROR: Failed to get create URL for StringBundle\n"); - } -#endif // We don't need to keep service around once we created the bundle nsServiceManager::ReleaseService(kStringBundleServiceCID, service); } else { @@ -612,6 +349,158 @@ nsEditor::PostCreate() return NS_OK; } +NS_IMETHODIMP +nsEditor::GetDocument(nsIDOMDocument **aDoc) +{ + if (!aDoc) + return NS_ERROR_NULL_POINTER; + *aDoc = nsnull; // init out param + NS_PRECONDITION(mDoc, "bad state, null mDoc"); + if (!mDoc) + return NS_ERROR_NOT_INITIALIZED; + return mDoc->QueryInterface(nsIDOMDocument::GetIID(), (void **)aDoc); +} + + +nsresult +nsEditor::GetPresShell(nsIPresShell **aPS) +{ + if (!aPS) + return NS_ERROR_NULL_POINTER; + *aPS = nsnull; // init out param + NS_PRECONDITION(mPresShell, "bad state, null mPresShell"); + if (!mPresShell) + return NS_ERROR_NOT_INITIALIZED; + return mPresShell->QueryInterface(nsIPresShell::GetIID(), (void **)aPS); +} + + +NS_IMETHODIMP +nsEditor::GetSelection(nsIDOMSelection **aSelection) +{ + if (!aSelection) + return NS_ERROR_NULL_POINTER; + *aSelection = nsnull; + nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, aSelection); // does an addref + return result; +} + +static NS_DEFINE_IID(kCFileWidgetCID, NS_FILEWIDGET_CID); + +NS_IMETHODIMP nsEditor::SaveDocument(PRBool saveAs, PRBool saveCopy) +{ + nsresult rv = NS_OK; + + // get the document + nsCOMPtr doc; + rv = GetDocument(getter_AddRefs(doc)); + if (NS_FAILED(rv) || !doc) + return rv; + + nsCOMPtr diskDoc = do_QueryInterface(doc); + if (!diskDoc) + return NS_ERROR_NO_INTERFACE; + + // this should really call out to the appcore for the display of the put file + // dialog. + + // find out if the doc already has a fileSpec associated with it. + nsFileSpec docFileSpec; + PRBool mustShowFileDialog = saveAs || (diskDoc->GetFileSpec(docFileSpec) == NS_ERROR_NOT_INITIALIZED); + PRBool replacing = !saveAs; + + if (mustShowFileDialog) + { + nsCOMPtr fileWidget; + rv = nsComponentManager::CreateInstance(kCFileWidgetCID, nsnull, nsIFileWidget::GetIID(), getter_AddRefs(fileWidget)); + if (NS_SUCCEEDED(rv) && fileWidget) + { + nsAutoString promptString("Save this document as:"); // XXX i18n, l10n + nsFileDlgResults dialogResult; + dialogResult = fileWidget->PutFile(nsnull, promptString, docFileSpec); + if (dialogResult == nsFileDlgResults_Cancel) + return NS_OK; + + replacing = (dialogResult == nsFileDlgResults_Replace); + } + else + { + NS_ASSERTION(0, "Failed to get file widget"); + return rv; + } + } + + nsAutoString charsetStr("ISO-8859-1"); + rv = diskDoc->SaveFile(&docFileSpec, replacing, saveCopy, nsIDiskDocument::eSaveFileHTML, charsetStr); + + if (NS_FAILED(rv)) + { + // show some error dialog? + NS_WARNING("Saving file failed"); + } + + return rv; +} + +NS_IMETHODIMP nsEditor::Save() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->Save(); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult rv = SaveDocument(PR_FALSE, PR_FALSE); + if (NS_FAILED(rv)) + return rv; + + rv = DoAfterDocumentSave(); + return rv; +} + +NS_IMETHODIMP nsEditor::SaveAs(PRBool aSavingCopy) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SaveAs(aSavingCopy); +#endif // ENABLE_JS_EDITOR_LOG + + return SaveDocument(PR_TRUE, aSavingCopy); +} + + +NS_IMETHODIMP +nsEditor::Do(nsITransaction *aTxn) +{ + if (gNoisy) { printf("Editor::Do ----------\n"); } + nsresult result = NS_OK; + nsCOMPtrselection; + nsresult selectionResult = GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + if (aTxn) + { + if (mTxnMgr) { + result = mTxnMgr->Do(aTxn); + } + else { + result = aTxn->Do(); + } + + if (NS_SUCCEEDED(result)) + result = DoAfterDoTransaction(aTxn); + } + + selection->EndBatchChanges(); + } + + return result; +} + + NS_IMETHODIMP nsEditor::EnableUndo(PRBool aEnable) { @@ -621,7 +510,7 @@ nsEditor::EnableUndo(PRBool aEnable) { if (!mTxnMgr) { - result = gCompMgr->CreateInstance(kCTransactionManagerCID, + result = nsComponentManager::CreateInstance(kCTransactionManagerCID, nsnull, nsITransactionManager::GetIID(), getter_AddRefs(mTxnMgr)); if (NS_FAILED(result) || !mTxnMgr) { @@ -643,6 +532,43 @@ nsEditor::EnableUndo(PRBool aEnable) return result; } + +NS_IMETHODIMP +nsEditor::Undo(PRUint32 aCount) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->Undo(aCount); +#endif // ENABLE_JS_EDITOR_LOG + + if (gNoisy) { printf("Editor::Undo ----------\n"); } + nsresult result = NS_OK; + + BeginUpdateViewBatch(); + + if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) + { + PRUint32 i=0; + for ( ; iUndo(); + + if (NS_SUCCEEDED(result)) + result = DoAfterUndoTransaction(); + + if (NS_FAILED(result)) + break; + } + } + + EndUpdateViewBatch(); + + return result; +} + + NS_IMETHODIMP nsEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo) { aIsEnabled = ((PRBool)((nsITransactionManager *)0!=mTxnMgr.get())); @@ -658,6 +584,43 @@ NS_IMETHODIMP nsEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo) return NS_OK; } + +NS_IMETHODIMP +nsEditor::Redo(PRUint32 aCount) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->Redo(aCount); +#endif // ENABLE_JS_EDITOR_LOG + + if (gNoisy) { printf("Editor::Redo ----------\n"); } + nsresult result = NS_OK; + + BeginUpdateViewBatch(); + + if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) + { + PRUint32 i=0; + for ( ; iRedo(); + + if (NS_SUCCEEDED(result)) + result = DoAfterRedoTransaction(); + + if (NS_FAILED(result)) + break; + } + } + + EndUpdateViewBatch(); + + return result; +} + + NS_IMETHODIMP nsEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo) { aIsEnabled = ((PRBool)((nsITransactionManager *)0!=mTxnMgr.get())); @@ -673,10 +636,181 @@ NS_IMETHODIMP nsEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo) return NS_OK; } + +NS_IMETHODIMP +nsEditor::BeginTransaction() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->BeginTransaction(); +#endif // ENABLE_JS_EDITOR_LOG + + BeginUpdateViewBatch(); + + if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) + { + mTxnMgr->BeginBatch(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsEditor::EndTransaction() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->EndTransaction(); +#endif // ENABLE_JS_EDITOR_LOG + + if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) + { + mTxnMgr->EndBatch(); + } + + EndUpdateViewBatch(); + + return NS_OK; +} + +// XXX: the rule system should tell us which node to select all on (ie, the root, or the body) +NS_IMETHODIMP nsEditor::SelectAll() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SelectAll(); +#endif // ENABLE_JS_EDITOR_LOG + + if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + result = SelectEntireDocument(selection); + } + return result; +} + +NS_IMETHODIMP nsEditor::BeginningOfDocument() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->BeginningOfDocument(); +#endif // ENABLE_JS_EDITOR_LOG + + if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + nsCOMPtr nodeList; + nsAutoString bodyTag = "body"; + result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); + if ((NS_SUCCEEDED(result)) && nodeList) + { + PRUint32 count; + nodeList->GetLength(&count); + NS_VERIFY(1==count, "there is not exactly 1 body in the document!"); + nsCOMPtr bodyNode; + result = nodeList->Item(0, getter_AddRefs(bodyNode)); + if ((NS_SUCCEEDED(result)) && bodyNode) + { + // Get the first child of the body node: + nsCOMPtr firstNode = GetDeepFirstChild(bodyNode); + if (firstNode) + { + result = selection->Collapse(firstNode, 0); + ScrollIntoView(PR_TRUE); + } + } + } + } + return result; +} + +NS_IMETHODIMP nsEditor::EndOfDocument() +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->EndOfDocument(); +#endif // ENABLE_JS_EDITOR_LOG + + if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + nsCOMPtr nodeList; + nsAutoString bodyTag = "body"; + result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); + if ((NS_SUCCEEDED(result)) && nodeList) + { + PRUint32 count; + nodeList->GetLength(&count); + NS_VERIFY(1==count, "there is not exactly 1 body in the document!"); + nsCOMPtr bodyNode; + result = nodeList->Item(0, getter_AddRefs(bodyNode)); + if ((NS_SUCCEEDED(result)) && bodyNode) + { + nsCOMPtr lastChild = GetDeepLastChild(bodyNode); + if ((NS_SUCCEEDED(result)) && lastChild) + { + // See if the last child is a text node; if so, set offset: + PRUint32 offset = 0; + if (IsTextNode(lastChild)) + { + nsCOMPtr text (do_QueryInterface(lastChild)); + if (text) + text->GetLength(&offset); + } + result = selection->Collapse(lastChild, offset); + ScrollIntoView(PR_FALSE); + } + } + } + } + return result; +} + +NS_IMETHODIMP +nsEditor::GetDocumentModified(PRBool *outDocModified) +{ + if (!outDocModified) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr theDoc; + nsresult rv = GetDocument(getter_AddRefs(theDoc)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr diskDoc = do_QueryInterface(theDoc, &rv); + if (NS_FAILED(rv)) return rv; + + PRInt32 modCount = 0; + diskDoc->GetModCount(&modCount); + + *outDocModified = (modCount != 0); + return NS_OK; +} + +/* + NS_IMETHODIMP nsEditor::SetProperties(nsVoidArray * aPropList) { - return NS_OK; + return NS_ERROR_NOT_IMPLEMENTED; } @@ -684,9 +818,9 @@ nsEditor::SetProperties(nsVoidArray * aPropList) NS_IMETHODIMP nsEditor::GetProperties(nsVoidArray *aPropList) { - return NS_OK; + return NS_ERROR_NOT_IMPLEMENTED; } - +*/ NS_IMETHODIMP nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, const nsString& aValue) @@ -700,23 +834,6 @@ nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, cons } -NS_IMETHODIMP -nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement, - const nsString& aAttribute, - const nsString& aValue, - ChangeAttributeTxn ** aTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (nsnull != aElement) - { - result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(this, aElement, aAttribute, aValue, PR_FALSE); - } - } - return result; -} - NS_IMETHODIMP nsEditor::GetAttributeValue(nsIDOMElement *aElement, const nsString& aAttribute, @@ -749,24 +866,568 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute) return result; } -NS_IMETHODIMP -nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, - const nsString& aAttribute, - ChangeAttributeTxn ** aTxn) + +NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, + nsIDOMNode * aParent, + PRInt32 aPosition, + nsIDOMNode ** aNewNode) { - nsresult result = NS_ERROR_NULL_POINTER; - if (nsnull != aElement) + CreateElementTxn *txn; + nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition, &txn); + if (NS_SUCCEEDED(result)) { - result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) + result = Do(txn); + if (NS_SUCCEEDED(result)) { - nsAutoString value; - result = (*aTxn)->Init(this, aElement, aAttribute, value, PR_TRUE); + result = txn->GetNewNode(aNewNode); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::Do succeeded."); } } return result; } + +NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, + nsIDOMNode * aParent, + PRInt32 aPosition) +{ + PRInt32 i; + nsIEditActionListener *listener; + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillInsertNode(aNode, aParent, aPosition); + } + } + + InsertElementTxn *txn; + nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition, &txn); + if (NS_SUCCEEDED(result)) { + result = Do(txn); + } + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidInsertNode(aNode, aParent, aPosition, result); + } + } + + return result; +} + + +NS_IMETHODIMP +nsEditor::SplitNode(nsIDOMNode * aNode, + PRInt32 aOffset, + nsIDOMNode **aNewLeftNode) +{ + PRInt32 i; + nsIEditActionListener *listener; + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillSplitNode(aNode, aOffset); + } + } + + SplitElementTxn *txn; + nsresult result = CreateTxnForSplitNode(aNode, aOffset, &txn); + if (NS_SUCCEEDED(result)) + { + result = Do(txn); + if (NS_SUCCEEDED(result)) + { + result = txn->GetNewNode(aNewLeftNode); + NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode"); + } + } + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + { + nsIDOMNode *ptr = (aNewLeftNode) ? *aNewLeftNode : 0; + listener->DidSplitNode(aNode, aOffset, ptr, result); + } + } + } + + return result; +} + + +NS_IMETHODIMP +nsEditor::JoinNodes(nsIDOMNode * aLeftNode, + nsIDOMNode * aRightNode, + nsIDOMNode * aParent) +{ + PRInt32 i; + nsIEditActionListener *listener; + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillJoinNodes(aLeftNode, aRightNode, aParent); + } + } + + JoinElementTxn *txn; + nsresult result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn); + if (NS_SUCCEEDED(result)) { + result = Do(txn); + } + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidJoinNodes(aLeftNode, aRightNode, aParent, result); + } + } + + return result; +} + + +NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) +{ + PRInt32 i; + nsIEditActionListener *listener; + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillDeleteNode(aElement); + } + } + + DeleteElementTxn *txn; + nsresult result = CreateTxnForDeleteElement(aElement, &txn); + if (NS_SUCCEEDED(result)) { + result = Do(txn); + } + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidDeleteNode(aElement, result); + } + } + + return result; +} + + +NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags) +{ + // these should be implemented by derived classes. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags) +{ + // these should be implemented by derived classes. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsEditor::AddEditActionListener(nsIEditActionListener *aListener) +{ + if (!aListener) + return NS_ERROR_NULL_POINTER; + + if (!mActionListeners) + { + mActionListeners = new nsVoidArray(); + + if (!mActionListeners) + return NS_ERROR_OUT_OF_MEMORY; + } + + if (!mActionListeners->AppendElement((void *)aListener)) + return NS_ERROR_FAILURE; + + NS_ADDREF(aListener); + + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener) +{ + if (!aListener || !mActionListeners) + return NS_ERROR_FAILURE; + + if (!mActionListeners->RemoveElement((void *)aListener)) + return NS_ERROR_FAILURE; + + NS_IF_RELEASE(aListener); + + if (mActionListeners->Count() < 1) + { + delete mActionListeners; + mActionListeners = 0; + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener) +{ + if (!aListener) + return NS_ERROR_NULL_POINTER; + + nsresult rv = NS_OK; + + if (!mDocStateListeners) + { + rv = NS_NewISupportsArray(getter_AddRefs(mDocStateListeners)); + if (NS_FAILED(rv)) return rv; + } + + nsCOMPtr iSupports = do_QueryInterface(aListener, &rv); + if (NS_FAILED(rv)) return rv; + + // is it already in the list? + PRInt32 foundIndex; + if (NS_SUCCEEDED(mDocStateListeners->GetIndexOf(iSupports, &foundIndex)) && foundIndex != -1) + return NS_OK; + + return mDocStateListeners->AppendElement(iSupports); +} + + +NS_IMETHODIMP +nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener) +{ + if (!aListener || !mDocStateListeners) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + nsCOMPtr iSupports = do_QueryInterface(aListener, &rv); + if (NS_FAILED(rv)) return rv; + + return mDocStateListeners->RemoveElement(iSupports); +} + + +NS_IMETHODIMP +nsEditor::DumpContentTree() +{ + nsCOMPtr thedoc; + nsCOMPtr presShell; + if (NS_SUCCEEDED(GetPresShell(getter_AddRefs(presShell)))) + { + presShell->GetDocument(getter_AddRefs(thedoc)); + if (thedoc) { + nsIContent* root = thedoc->GetRootContent(); + if (nsnull != root) { + root->List(stdout); + NS_RELEASE(root); + } + } + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::DebugDumpContent() const +{ + nsCOMPtrcontent; + nsCOMPtrnodeList; + nsAutoString bodyTag = "body"; + mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); + if (nodeList) + { + PRUint32 count; + nodeList->GetLength(&count); + NS_ASSERTION(1==count, "there is not exactly 1 body in the document!"); + nsCOMPtrbodyNode; + nodeList->Item(0, getter_AddRefs(bodyNode)); + if (bodyNode) { + content = do_QueryInterface(bodyNode); + } + } + content->List(); + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) +{ + NS_NOTREACHED("This should never get called. Overridden by subclasses"); + return NS_OK; +} + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorIMESupport --- +#pragma mark - +#endif + +// +// The BeingComposition method is called from the Editor Composition event listeners. +// It caches the current text node and offset which is subsequently used for the +// created of IMETextTxn's. +// +NS_IMETHODIMP +nsEditor::BeginComposition(void) +{ +#ifdef DEBUG_tague + printf("nsEditor::StartComposition\n"); +#endif + nsresult result; + PRInt32 offset; + nsCOMPtr selection; + nsCOMPtr nodeAsText; + + result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + result = NS_ERROR_UNEXPECTED; + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && (currentItem)) + { + result = NS_ERROR_UNEXPECTED; + nsCOMPtr range(do_QueryInterface(currentItem)); + if (range) + { + nsCOMPtr node; + result = range->GetStartParent(getter_AddRefs(node)); + if ((NS_SUCCEEDED(result)) && (node)) + { + nodeAsText = do_QueryInterface(node); + range->GetStartOffset(&offset); + if (!nodeAsText) { + result = NS_ERROR_EDITOR_NO_TEXTNODE; + } + } + } + } + else + { + result = NS_ERROR_EDITOR_NO_SELECTION; + } + } + } + + if (NS_SUCCEEDED(result) && nodeAsText) + { + // + // store the information needed to construct IME transactions for this composition + // + mIMETextNode = nodeAsText; + mIMETextOffset = offset; + mIMEBufferLength = 0; + } + + return result; +} + +NS_IMETHODIMP +nsEditor::EndComposition(void) +{ + nsresult result; + IMECommitTxn *commitTxn; + + // + // create the commit transaction..we can do it directly from the transaction mgr + // + result = TransactionFactory::GetNewTransaction(IMECommitTxn::GetCID(), (EditTxn**)&commitTxn); + if (NS_SUCCEEDED(result) && commitTxn!=nsnull) + { + commitTxn->Init(); + result = Do(commitTxn); + } + + /* reset the data we need to construct a transaction */ + mIMETextNode = do_QueryInterface(nsnull); + mIMETextOffset = 0; + mIMEBufferLength = 0; + + return result; +} + +NS_IMETHODIMP +nsEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply) +{ + nsCOMPtr caretP; + nsresult result = SetInputMethodText(aCompositionString,aTextRangeList); + mIMEBufferLength = aCompositionString.Length(); + + mPresShell->GetCaret(getter_AddRefs(caretP)); + caretP->GetWindowRelativeCoordinates(aReply->mCursorPosition,aReply->mCursorIsCollapsed); + + return result; +} + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorLogging --- +#pragma mark - +#endif + + +NS_IMETHODIMP +nsEditor::StartLogging(nsIFileSpec *aLogFile) +{ +#ifdef ENABLE_JS_EDITOR_LOG + + mJSEditorLog = new nsJSEditorLog(this, aLogFile); + + if (!mJSEditorLog) + return NS_ERROR_OUT_OF_MEMORY; + + if (mTxnMgr) + { + mJSTxnLog = new nsJSTxnLog(mJSEditorLog); + + if (mJSTxnLog) + { + NS_ADDREF(mJSTxnLog); + mTxnMgr->AddListener(mJSTxnLog); + } + else + return NS_ERROR_OUT_OF_MEMORY; + } + +#endif // ENABLE_JS_EDITOR_LOG + + return NS_OK; +} + +NS_IMETHODIMP +nsEditor::StopLogging() +{ +#ifdef ENABLE_JS_EDITOR_LOG + + if (mTxnMgr && mJSTxnLog) + mTxnMgr->RemoveListener(mJSTxnLog); + + if (mJSTxnLog) + { + NS_RELEASE(mJSTxnLog); + mJSTxnLog = 0; + } + + if (mJSEditorLog) + { + delete mJSEditorLog; + mJSEditorLog = 0; + } + +#endif // ENABLE_JS_EDITOR_LOG + + return NS_OK; +} + + + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- public nsEditor methods --- +#pragma mark - +#endif +/* Non-interface, public methods */ + + +// This seems like too much work! There should be a "nsDOMDocument::GetBody()" +// Have a look in nsHTMLDocument. Maybe add it to nsIHTMLDocument. +NS_IMETHODIMP +nsEditor::GetBodyElement(nsIDOMElement **aBodyElement) +{ + nsresult result; + + if (!aBodyElement) + return NS_ERROR_NULL_POINTER; + + *aBodyElement = 0; + + NS_PRECONDITION(mDoc, "bad state, null mDoc"); + if (!mDoc) + return NS_ERROR_NOT_INITIALIZED; + + nsCOMPtrnodeList; + nsString bodyTag = "body"; + + result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); + + if (NS_FAILED(result)) + return result; + + if (!nodeList) + return NS_ERROR_NULL_POINTER; + + PRUint32 count; + nodeList->GetLength(&count); + + NS_ASSERTION(1==count, "More than one body found in document!"); + + if (count < 1) + return NS_ERROR_FAILURE; + + // Use the first body node in the list: + nsCOMPtr node; + result = nodeList->Item(0, getter_AddRefs(node)); + if (NS_SUCCEEDED(result) && node) + { + //return node->QueryInterface(nsIDOMElement::GetIID(), (void **)aBodyElement); + // Is above equivalent to this: + nsCOMPtr bodyElement = do_QueryInterface(node); + if (bodyElement) + { + *aBodyElement = bodyElement; + // A "getter" method should always addref + NS_ADDREF(*aBodyElement); + } + } + return result; +} + + // Objects must be DOM elements NS_IMETHODIMP nsEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) @@ -840,235 +1501,88 @@ nsEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) return result; } -NS_IMETHODIMP -nsEditor::InsertBreak() -{ - return NS_OK; -} - -//END nsIEditorInterfaces - - -//BEGIN nsEditor Private methods - -NS_IMETHODIMP -nsEditor::DoAfterDoTransaction(nsITransaction *aTxn) -{ - nsresult rv = NS_OK; - - PRBool isTransientTransaction; - rv = aTxn->GetIsTransient(&isTransientTransaction); - if (NS_FAILED(rv)) - return rv; - - if (!isTransientTransaction) - { - // we need to deal here with the case where the user saved after some - // edits, then undid one or more times. Then, the undo count is -ve, - // but we can't let a do take it back to zero. So we flip it up to - // a +ve number. - PRInt32 modCount; - GetDocModCount(modCount); - if (modCount < 0) - modCount = -modCount; - - rv = IncDocModCount(1); // don't count transient transactions - } - - return rv; -} - - -NS_IMETHODIMP -nsEditor::DoAfterUndoTransaction() -{ - nsresult rv = NS_OK; - - rv = IncDocModCount(-1); // all undoable transactions are non-transient - - return rv; -} - -NS_IMETHODIMP -nsEditor::DoAfterRedoTransaction() -{ - nsresult rv = NS_OK; - - rv = IncDocModCount(1); // all redoable transactions are non-transient - - return rv; -} - -NS_IMETHODIMP -nsEditor::DoAfterDocumentSave() -{ - // the mod count is reset by nsIDiskDocument. - NotifyDocumentListeners(eDocumentStateChanged); - return NS_OK; -} - - -NS_IMETHODIMP -nsEditor::Do(nsITransaction *aTxn) -{ - if (gNoisy) { printf("Editor::Do ----------\n"); } - nsresult result = NS_OK; - nsCOMPtrselection; - nsresult selectionResult = GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(selectionResult) && selection) { - selection->StartBatchChanges(); - if (aTxn) - { - if (mTxnMgr) { - result = mTxnMgr->Do(aTxn); - } - else { - result = aTxn->Do(); - } - - if (NS_SUCCEEDED(result)) - result = DoAfterDoTransaction(aTxn); - } - - selection->EndBatchChanges(); - } - - return result; -} - -NS_IMETHODIMP -nsEditor::Undo(PRUint32 aCount) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Undo(aCount); -#endif // ENABLE_JS_EDITOR_LOG - - if (gNoisy) { printf("Editor::Undo ----------\n"); } - nsresult result = NS_OK; - - BeginUpdateViewBatch(); - - if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) - { - PRUint32 i=0; - for ( ; iUndo(); - - if (NS_SUCCEEDED(result)) - result = DoAfterUndoTransaction(); - - if (NS_FAILED(result)) - break; - } - } - - EndUpdateViewBatch(); - - return result; -} - -NS_IMETHODIMP -nsEditor::Redo(PRUint32 aCount) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Redo(aCount); -#endif // ENABLE_JS_EDITOR_LOG - - if (gNoisy) { printf("Editor::Redo ----------\n"); } - nsresult result = NS_OK; - - BeginUpdateViewBatch(); - - if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) - { - PRUint32 i=0; - for ( ; iRedo(); - - if (NS_SUCCEEDED(result)) - result = DoAfterRedoTransaction(); - - if (NS_FAILED(result)) - break; - } - } - - EndUpdateViewBatch(); - - return result; -} - -NS_IMETHODIMP -nsEditor::BeginTransaction() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->BeginTransaction(); -#endif // ENABLE_JS_EDITOR_LOG - - BeginUpdateViewBatch(); - - if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) - { - mTxnMgr->BeginBatch(); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::EndTransaction() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->EndTransaction(); -#endif // ENABLE_JS_EDITOR_LOG - - if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) - { - mTxnMgr->EndBatch(); - } - - EndUpdateViewBatch(); - - return NS_OK; -} +#ifdef XP_MAC +#pragma mark - +#pragma mark --- Protected and static methods --- +#pragma mark - +#endif NS_IMETHODIMP nsEditor::ScrollIntoView(PRBool aScrollToBegin) { - return NS_OK; + return NS_ERROR_NOT_IMPLEMENTED; } -// XXX: the rule system should tell us which node to select all on (ie, the root, or the body) -NS_IMETHODIMP nsEditor::SelectAll() + +nsString& nsEditor::GetTextNodeTag() +{ + static nsString gTextNodeTag("special text node tag"); + return gTextNodeTag; +} + + +NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) { #ifdef ENABLE_JS_EDITOR_LOG nsAutoJSEditorLogLock logLock(mJSEditorLog); if (mJSEditorLog) - mJSEditorLog->SelectAll(); + mJSEditorLog->InsertText(aStringToInsert); + #endif // ENABLE_JS_EDITOR_LOG - if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) + EditAggregateTxn *aggTxn = nsnull; + // Create the "delete current selection" txn + nsresult result = CreateAggregateTxnForDeleteSelection(InsertTextTxn::gInsertTextTxnName, &aggTxn); + if ((NS_FAILED(result)) || (nsnull==aggTxn)) { + return NS_ERROR_OUT_OF_MEMORY; + } + InsertTextTxn *txn; + result = CreateTxnForInsertText(aStringToInsert, nsnull, &txn); // insert at the current selection + if ((NS_SUCCEEDED(result)) && txn) { + BeginUpdateViewBatch(); + aggTxn->AppendChild(txn); + result = Do(aggTxn); + EndUpdateViewBatch(); + } + else if (NS_ERROR_EDITOR_NO_SELECTION==result) { + result = DoInitialInsert(aStringToInsert); + } + else if (NS_ERROR_EDITOR_NO_TEXTNODE==result) { - result = SelectEntireDocument(selection); + BeginTransaction(); + result = Do(aggTxn); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + result = GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + nsCOMPtr selectedNode; + PRInt32 offset; + result = selection->GetAnchorNode(getter_AddRefs(selectedNode)); + if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offset)) && selectedNode) + { + nsCOMPtr newNode; + result = CreateNode(GetTextNodeTag(), selectedNode, offset, + getter_AddRefs(newNode)); + if (NS_SUCCEEDED(result) && newNode) + { + nsCOMPtrnewTextNode; + newTextNode = do_QueryInterface(newNode); + if (newTextNode) + { + nsAutoString placeholderText(" "); + newTextNode->SetData(placeholderText); + selection->Collapse(newNode, 0); + selection->Extend(newNode, 1); + result = InsertTextImpl(aStringToInsert); // this really recurses, right? + } + } + } + } + } + EndTransaction(); } return result; } @@ -1134,45 +1648,6 @@ nsEditor::GetDeepFirstChild(nsCOMPtr aRoot) return deepFirstChildN; } -NS_IMETHODIMP nsEditor::BeginningOfDocument() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->BeginningOfDocument(); -#endif // ENABLE_JS_EDITOR_LOG - - if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) - { - nsCOMPtr nodeList; - nsAutoString bodyTag = "body"; - result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if ((NS_SUCCEEDED(result)) && nodeList) - { - PRUint32 count; - nodeList->GetLength(&count); - NS_VERIFY(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtr bodyNode; - result = nodeList->Item(0, getter_AddRefs(bodyNode)); - if ((NS_SUCCEEDED(result)) && bodyNode) - { - // Get the first child of the body node: - nsCOMPtr firstNode = GetDeepFirstChild(bodyNode); - if (firstNode) - { - result = selection->Collapse(firstNode, 0); - ScrollIntoView(PR_TRUE); - } - } - } - } - return result; -} nsCOMPtr nsEditor::GetDeepLastChild(nsCOMPtr aRoot) @@ -1212,430 +1687,7 @@ nsEditor::GetDeepLastChild(nsCOMPtr aRoot) return deepLastChildN; } -NS_IMETHODIMP nsEditor::EndOfDocument() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - if (mJSEditorLog) - mJSEditorLog->EndOfDocument(); -#endif // ENABLE_JS_EDITOR_LOG - - if (!mDoc || !mPresShell) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - nsresult result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) - { - nsCOMPtr nodeList; - nsAutoString bodyTag = "body"; - result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if ((NS_SUCCEEDED(result)) && nodeList) - { - PRUint32 count; - nodeList->GetLength(&count); - NS_VERIFY(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtr bodyNode; - result = nodeList->Item(0, getter_AddRefs(bodyNode)); - if ((NS_SUCCEEDED(result)) && bodyNode) - { - nsCOMPtr lastChild = GetDeepLastChild(bodyNode); - if ((NS_SUCCEEDED(result)) && lastChild) - { - // See if the last child is a text node; if so, set offset: - PRUint32 offset = 0; - if (IsTextNode(lastChild)) - { - nsCOMPtr text (do_QueryInterface(lastChild)); - if (text) - text->GetLength(&offset); - } - result = selection->Collapse(lastChild, offset); - ScrollIntoView(PR_FALSE); - } - } - } - } - return result; -} - -NS_IMETHODIMP nsEditor::Cut() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Cut(); -#endif // ENABLE_JS_EDITOR_LOG - - nsCOMPtr selection; - nsresult res = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (!NS_SUCCEEDED(res)) - return res; - - PRBool isCollapsed; - if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed) - return NS_ERROR_NOT_AVAILABLE; - - res = Copy(); - if (NS_SUCCEEDED(res)) - res = DeleteSelection(eDoNothing); - return res; -} - - -NS_IMETHODIMP nsEditor::Copy() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Copy(); -#endif // ENABLE_JS_EDITOR_LOG - - //printf("nsEditor::Copy\n"); - - return mPresShell->DoCopy(); -} - -NS_IMETHODIMP nsEditor::Paste() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Paste(); -#endif // ENABLE_JS_EDITOR_LOG - - //printf("nsEditor::Paste\n"); - nsString stuffToPaste; - - // Get Clipboard Service - nsIClipboard* clipboard; - nsresult rv = nsServiceManager::GetService(kCClipboardCID, - nsIClipboard::GetIID(), - (nsISupports **)&clipboard); - - // Create generic Transferable for getting the data - nsCOMPtr trans; - rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, - nsITransferable::GetIID(), - (void**) getter_AddRefs(trans)); - if (NS_SUCCEEDED(rv)) - { - // Get the nsITransferable interface for getting the data from the clipboard - if (trans) - { - // The only data type we support is plaintext; - // derived classes will support other types. - nsAutoString textFlavor(kTextMime); - trans->AddDataFlavor(&textFlavor); - - // Get the Data from the clipboard - if (NS_SUCCEEDED(clipboard->GetData(trans))) - { - nsAutoString flavor; - char * data; - PRUint32 len; - if (NS_SUCCEEDED(trans->GetAnyTransferData(&flavor, (void **)&data, &len))) - { -#ifdef DEBUG - printf("Got flavor [%s]\n", flavor.ToNewCString()); -#endif - if (flavor.Equals(textFlavor)) - { - if (data && len > 0) // stuffToPaste is ready for insertion into the content - { - stuffToPaste.SetString(data, len); - rv = InsertText(stuffToPaste); - } - } - } - - } - } - } - nsServiceManager::ReleaseService(kCClipboardCID, clipboard); - - return rv; -} - -NS_IMETHODIMP nsEditor::PasteAsQuotation() -{ -#ifdef DEBUG - printf("nsEditor::PasteAsQuotation() not meaningful, shouldn't be here\n"); -#endif - return Paste(); -} - -NS_IMETHODIMP nsEditor::InsertAsQuotation(const nsString& aQuotedText) -{ -#ifdef DEBUG - printf("nsEditor::PasteAsQuotation() not meaningful, shouldn't be here\n"); -#endif - return InsertText(aQuotedText); -} - -NS_IMETHODIMP -nsEditor::AddStyleSheet(nsICSSStyleSheet* aSheet) -{ - AddStyleSheetTxn* aTxn; - nsresult rv = CreateTxnForAddStyleSheet(aSheet, &aTxn); - if (NS_SUCCEEDED(rv) && aTxn) - { - rv = Do(aTxn); - if (NS_SUCCEEDED(rv)) - { - mLastStyleSheet = do_QueryInterface(aSheet); // save it so we can remove before applying the next one - } - } - - return rv; -} - - -NS_IMETHODIMP -nsEditor::RemoveStyleSheet(nsICSSStyleSheet* aSheet) -{ - RemoveStyleSheetTxn* aTxn; - nsresult rv = CreateTxnForRemoveStyleSheet(aSheet, &aTxn); - if (NS_SUCCEEDED(rv) && aTxn) - { - rv = Do(aTxn); - if (NS_SUCCEEDED(rv)) - { - mLastStyleSheet = nsnull; // forget it - } - } - - return rv; -} - -NS_IMETHODIMP -nsEditor::ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet) -{ - nsresult rv = NS_OK; - - BeginTransaction(); - - if (mLastStyleSheet) - { - rv = RemoveStyleSheet(mLastStyleSheet); - } - - rv = AddStyleSheet(aNewSheet); - - EndTransaction(); - - return rv; -} - -/* static */ -void nsEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData) -{ - nsresult rv = NS_OK; - - nsEditor *editor = NS_STATIC_CAST(nsEditor*, aData); - if (editor) - { - rv = editor->ReplaceStyleSheet(aSheet); - } - - // we lose the return value here. Set a flag in the editor? -} - -NS_IMETHODIMP nsEditor::ApplyStyleSheet(const nsString& aURL) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->ApplyStyleSheet(aURL); -#endif // ENABLE_JS_EDITOR_LOG - - // XXX: Note that this is not an undo-able action yet! - - nsresult rv = NS_OK; - nsIURI* uaURL = 0; - -#ifndef NECKO - rv = NS_NewURL(&uaURL, aURL); -#else - rv = NS_NewURI(&uaURL, aURL); -#endif // NECKO - - if (NS_SUCCEEDED(rv)) { - nsCOMPtr document; - - rv = mPresShell->GetDocument(getter_AddRefs(document)); - - if (NS_SUCCEEDED(rv)) { - if (document) { - nsCOMPtr container = do_QueryInterface(document); - - if (container) { - nsICSSLoader *cssLoader = 0; - nsICSSStyleSheet *cssStyleSheet = 0; - - rv = container->GetCSSLoader(cssLoader); - - if (NS_SUCCEEDED(rv)) { - if (cssLoader) { - PRBool complete; - - rv = cssLoader->LoadAgentSheet(uaURL, cssStyleSheet, complete, - nsEditor::ApplyStyleSheetToPresShellDocument, - this); - - if (NS_SUCCEEDED(rv)) { - if (complete) { - if (cssStyleSheet) { - nsEditor::ApplyStyleSheetToPresShellDocument(cssStyleSheet, - this); - } - else - rv = NS_ERROR_NULL_POINTER; - } - - // - // If not complete, we will be notified later - // with a call to AddStyleSheetToEditorDocument(). - // - } - } - else - rv = NS_ERROR_NULL_POINTER; - } - } - else - rv = NS_ERROR_NULL_POINTER; - } - else - rv = NS_ERROR_NULL_POINTER; - } - - NS_RELEASE(uaURL); - } - - return rv; -} - -NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsEditor::DumpContentTree() -{ - nsCOMPtr thedoc; - nsCOMPtr presShell; - if (NS_SUCCEEDED(GetPresShell(getter_AddRefs(presShell)))) - { - presShell->GetDocument(getter_AddRefs(thedoc)); - if (thedoc) { - nsIContent* root = thedoc->GetRootContent(); - if (nsnull != root) { - root->List(stdout); - NS_RELEASE(root); - } - } - } - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::AddEditActionListener(nsIEditActionListener *aListener) -{ - if (!aListener) - return NS_ERROR_NULL_POINTER; - - if (!mActionListeners) - { - mActionListeners = new nsVoidArray(); - - if (!mActionListeners) - return NS_ERROR_OUT_OF_MEMORY; - } - - if (!mActionListeners->AppendElement((void *)aListener)) - return NS_ERROR_FAILURE; - - NS_ADDREF(aListener); - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener) -{ - if (!aListener || !mActionListeners) - return NS_ERROR_FAILURE; - - if (!mActionListeners->RemoveElement((void *)aListener)) - return NS_ERROR_FAILURE; - - NS_IF_RELEASE(aListener); - - if (mActionListeners->Count() < 1) - { - delete mActionListeners; - mActionListeners = 0; - } - - return NS_OK; -} - - -NS_IMETHODIMP -nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener) -{ - if (!aListener) - return NS_ERROR_NULL_POINTER; - - nsresult rv = NS_OK; - - if (!mDocStateListeners) - { - rv = NS_NewISupportsArray(getter_AddRefs(mDocStateListeners)); - if (NS_FAILED(rv)) return rv; - } - - nsCOMPtr iSupports = do_QueryInterface(aListener, &rv); - if (NS_FAILED(rv)) return rv; - - // is it already in the list? - PRInt32 foundIndex; - if (NS_SUCCEEDED(mDocStateListeners->GetIndexOf(iSupports, &foundIndex)) && foundIndex != -1) - return NS_OK; - - return mDocStateListeners->AppendElement(iSupports); -} - - -NS_IMETHODIMP -nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener) -{ - if (!aListener || !mDocStateListeners) - return NS_ERROR_NULL_POINTER; - - nsresult rv; - nsCOMPtr iSupports = do_QueryInterface(aListener, &rv); - if (NS_FAILED(rv)) return rv; - - return mDocStateListeners->RemoveElement(iSupports); -} NS_IMETHODIMP nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType) @@ -1710,270 +1762,6 @@ nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationTyp return rv; } -NS_IMETHODIMP -nsEditor::GetDocumentModified(PRBool *outDocModified) -{ - if (!outDocModified) - return NS_ERROR_NULL_POINTER; - - nsCOMPtr theDoc; - nsresult rv = GetDocument(getter_AddRefs(theDoc)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr diskDoc = do_QueryInterface(theDoc, &rv); - if (NS_FAILED(rv)) return rv; - - PRInt32 modCount = 0; - diskDoc->GetModCount(&modCount); - - *outDocModified = (modCount != 0); - return NS_OK; -} - -nsString & nsIEditor::GetTextNodeTag() -{ - static nsString gTextNodeTag("special text node tag"); - return gTextNodeTag; -} - -NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, - nsIDOMNode * aParent, - PRInt32 aPosition, - nsIDOMNode ** aNewNode) -{ - CreateElementTxn *txn; - nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition, &txn); - if (NS_SUCCEEDED(result)) - { - result = Do(txn); - if (NS_SUCCEEDED(result)) - { - result = txn->GetNewNode(aNewNode); - NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::Do succeeded."); - } - } - return result; -} - -NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsString& aTag, - nsIDOMNode *aParent, - PRInt32 aPosition, - CreateElementTxn ** aTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (nsnull != aParent) - { - result = TransactionFactory::GetNewTransaction(CreateElementTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(this, aTag, aParent, aPosition); - } - } - return result; -} - -NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, - nsIDOMNode * aParent, - PRInt32 aPosition) -{ - PRInt32 i; - nsIEditActionListener *listener; - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->WillInsertNode(aNode, aParent, aPosition); - } - } - - InsertElementTxn *txn; - nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); - } - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->DidInsertNode(aNode, aParent, aPosition, result); - } - } - - return result; -} - -NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode, - nsIDOMNode * aParent, - PRInt32 aPosition, - InsertElementTxn ** aTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (aNode && aParent && aTxn) - { - result = TransactionFactory::GetNewTransaction(InsertElementTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(aNode, aParent, aPosition, this); - } - } - return result; -} - -NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) -{ - PRInt32 i; - nsIEditActionListener *listener; - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->WillDeleteNode(aElement); - } - } - - DeleteElementTxn *txn; - nsresult result = CreateTxnForDeleteElement(aElement, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); - } - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->DidDeleteNode(aElement, result); - } - } - - return result; -} - -NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement, - DeleteElementTxn ** aTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (nsnull != aElement) - { - result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn); - if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(aElement); - } - } - return result; -} - -NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) -{ - nsresult result = NS_ERROR_NULL_POINTER; - if (aAggTxn) - { - *aAggTxn = nsnull; - result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn**)aAggTxn); - - if (NS_FAILED(result) || !*aAggTxn) { - return NS_ERROR_OUT_OF_MEMORY; - } - - // Set the name for the aggregate transaction - (*aAggTxn)->SetName(aTxnName); - - // Get current selection and setup txn to delete it, - // but only if selection exists (is not a collapsed "caret" state) - nsCOMPtr selection; - result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) - { - PRBool collapsed; - result = selection->GetIsCollapsed(&collapsed); - if (NS_SUCCEEDED(result) && !collapsed) { - EditAggregateTxn *delSelTxn; - result = CreateTxnForDeleteSelection(nsIEditor::eDoNothing, - &delSelTxn); - if (NS_SUCCEEDED(result) && delSelTxn) { - (*aAggTxn)->AppendChild(delSelTxn); - } - } - } - } - return result; -} - - -NS_IMETHODIMP -nsEditor::InsertText(const nsString& aStringToInsert) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertText(aStringToInsert); - -#endif // ENABLE_JS_EDITOR_LOG - - EditAggregateTxn *aggTxn = nsnull; - // Create the "delete current selection" txn - nsresult result = CreateAggregateTxnForDeleteSelection(InsertTextTxn::gInsertTextTxnName, &aggTxn); - if ((NS_FAILED(result)) || (nsnull==aggTxn)) { - return NS_ERROR_OUT_OF_MEMORY; - } - InsertTextTxn *txn; - result = CreateTxnForInsertText(aStringToInsert, nsnull, &txn); // insert at the current selection - if ((NS_SUCCEEDED(result)) && txn) { - BeginUpdateViewBatch(); - aggTxn->AppendChild(txn); - result = Do(aggTxn); - EndUpdateViewBatch(); - } - else if (NS_ERROR_EDITOR_NO_SELECTION==result) { - result = DoInitialInsert(aStringToInsert); - } - else if (NS_ERROR_EDITOR_NO_TEXTNODE==result) - { - BeginTransaction(); - result = Do(aggTxn); - if (NS_SUCCEEDED(result)) - { - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - nsCOMPtr selectedNode; - PRInt32 offset; - result = selection->GetAnchorNode(getter_AddRefs(selectedNode)); - if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offset)) && selectedNode) - { - nsCOMPtr newNode; - result = CreateNode(GetTextNodeTag(), selectedNode, offset, - getter_AddRefs(newNode)); - if (NS_SUCCEEDED(result) && newNode) - { - nsCOMPtrnewTextNode; - newTextNode = do_QueryInterface(newNode); - if (newTextNode) - { - nsAutoString placeholderText(" "); - newTextNode->SetData(placeholderText); - selection->Collapse(newNode, 0); - selection->Extend(newNode, 1); - result = InsertText(aStringToInsert); - } - } - } - } - } - EndTransaction(); - } - return result; -} NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert, nsIDOMCharacterData *aTextNode, @@ -2155,460 +1943,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData *aElement, } -NS_IMETHODIMP nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag, - nsIDOMNode ** aNewNode) -{ - nsCOMPtr parentSelectedNode; - PRInt32 offsetOfNewNode; - nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, - offsetOfNewNode); - if (!NS_SUCCEEDED(result)) - return result; - nsCOMPtr newNode; - result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode, - getter_AddRefs(newNode)); - - *aNewNode = newNode; - - // we want the selection to be just after the new node - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - selection->Collapse(parentSelectedNode, offsetOfNewNode+1); - - return result; -} - -NS_IMETHODIMP nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode) -{ - nsresult result=NS_ERROR_NOT_INITIALIZED; - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - PRBool collapsed; - result = selection->GetIsCollapsed(&collapsed); - if (NS_SUCCEEDED(result) && !collapsed) - { - result = DeleteSelection(nsIEditor::eDoNothing); - if (NS_FAILED(result)) { - return result; - } - // get the new selection - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) { - return result; - } -#ifdef NS_DEBUG - nsCOMPtrtestSelectedNode; - nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode)); - // no selection is ok. - // if there is a selection, it must be collapsed - if (testSelectedNode) - { - PRBool testCollapsed; - debugResult = selection->GetIsCollapsed(&testCollapsed); - NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); - NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion"); - } -#endif - } - // split the selected node - PRInt32 offsetOfSelectedNode; - result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); - if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode) - { - nsCOMPtr selectedNode; - PRUint32 selectedNodeContentCount=0; - nsCOMPtrselectedParentNodeAsText; - selectedParentNodeAsText = do_QueryInterface(parentSelectedNode); - - /* if the selection is a text node, split the text node if necesary - and compute where to put the new node - */ - if (selectedParentNodeAsText) - { - PRInt32 indexOfTextNodeInParent; - selectedNode = do_QueryInterface(parentSelectedNode); - selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode)); - selectedParentNodeAsText->GetLength(&selectedNodeContentCount); - GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent); - - if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) - { - nsCOMPtr newSiblingNode; - result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); - // now get the node's offset in it's parent, and insert the new tag there - if (NS_SUCCEEDED(result)) { - result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - } - } - else - { // determine where to insert the new node - if (0==offsetOfSelectedNode) { - offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent - } - else { // insert new node as last child - GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node - } - } - } - /* if the selection is not a text node, split the parent node if necesary - and compute where to put the new node - */ - else - { // it's an interior node - nsCOMPtrparentChildList; - parentSelectedNode->GetChildNodes(getter_AddRefs(parentChildList)); - if ((NS_SUCCEEDED(result)) && parentChildList) - { - result = parentChildList->Item(offsetOfSelectedNode, getter_AddRefs(selectedNode)); - if ((NS_SUCCEEDED(result)) && selectedNode) - { - nsCOMPtrselectedNodeAsText; - selectedNodeAsText = do_QueryInterface(selectedNode); - nsCOMPtrchildList; - //CM: I added "result =" - result = selectedNode->GetChildNodes(getter_AddRefs(childList)); - if (NS_SUCCEEDED(result)) - { - if (childList) - { - childList->GetLength(&selectedNodeContentCount); - } - else - { - // This is the case where the collapsed selection offset - // points to an inline node with no children - // This must also be where the new node should be inserted - // and there is no splitting necessary - offsetOfNewNode = offsetOfSelectedNode; - return NS_OK; - } - } - else - { - return NS_ERROR_FAILURE; - } - if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) - { - nsCOMPtr newSiblingNode; - result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); - // now get the node's offset in it's parent, and insert the new tag there - if (NS_SUCCEEDED(result)) { - result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - } - } - else - { // determine where to insert the new node - if (0==offsetOfSelectedNode) { - offsetOfNewNode = 0; // insert new node as first child - } - else { // insert new node as last child - GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node - } - } - } - } - } - - // Here's where the new node was inserted - } - else { - printf("InsertBreak into an empty document is not yet supported\n"); - } - } - return result; -} - -NS_IMETHODIMP -nsEditor::DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->DeleteSelection(aAction); -#endif // ENABLE_JS_EDITOR_LOG - - nsresult result; - - EditAggregateTxn *txn; - result = CreateTxnForDeleteSelection(aAction, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); - } - - return result; -} - -NS_IMETHODIMP nsEditor::CreateTxnForDeleteSelection(nsIEditor::ECollapsedSelectionAction aAction, - EditAggregateTxn ** aTxn) -{ - if (!aTxn) - return NS_ERROR_NULL_POINTER; - *aTxn = nsnull; - - nsresult result; - nsCOMPtr selection; - result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - // Check whether the selection is collapsed and we should do nothing: - PRBool isCollapsed; - result = (selection->GetIsCollapsed(&isCollapsed)); - if (NS_SUCCEEDED(result) && isCollapsed && aAction == eDoNothing) - return NS_OK; - - // allocate the out-param transaction - result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn **)aTxn); - if (NS_FAILED(result)) { - return result; - } - - nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(result) && enumerator) - { - for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next()) - { - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(result)) && (currentItem)) - { - nsCOMPtr range( do_QueryInterface(currentItem) ); - range->GetIsCollapsed(&isCollapsed); - if (PR_FALSE==isCollapsed) - { - DeleteRangeTxn *txn; - result = TransactionFactory::GetNewTransaction(DeleteRangeTxn::GetCID(), (EditTxn **)&txn); - if ((NS_SUCCEEDED(result)) && (nsnull!=txn)) - { - txn->Init(this, range); - (*aTxn)->AppendChild(txn); - } - else - result = NS_ERROR_OUT_OF_MEMORY; - } - else - { // we have an insertion point. delete the thing in front of it or behind it, depending on aAction - result = CreateTxnForDeleteInsertionPoint(range, aAction, *aTxn); - } - } - } - } - } - - // if we didn't build the transaction correctly, destroy the out-param transaction so we don't leak it. - if (NS_FAILED(result)) - { - NS_IF_RELEASE(*aTxn); - } - - return result; -} - - -//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented -NS_IMETHODIMP -nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, - nsIEditor::ECollapsedSelectionAction - aAction, - EditAggregateTxn *aTxn) -{ - nsCOMPtr node; - PRBool isFirst; - PRBool isLast; - PRInt32 offset; - //PRInt32 length=1; - - // get the node and offset of the insertion point - nsresult result = aRange->GetStartParent(getter_AddRefs(node)); - if (NS_FAILED(result)) - return result; - result = aRange->GetStartOffset(&offset); - if (NS_FAILED(result)) - return result; - - // determine if the insertion point is at the beginning, middle, or end of the node - nsCOMPtr nodeAsText; - nsCOMPtr selectedNode; - nodeAsText = do_QueryInterface(node); - - if (nodeAsText) - { - PRUint32 count; - nodeAsText->GetLength(&count); - isFirst = PRBool(0==offset); - isLast = PRBool(count==(PRUint32)offset); - } - else - { - // get the child list and count - nsCOMPtrchildList; - PRUint32 count=0; - result = node->GetChildNodes(getter_AddRefs(childList)); - if ((NS_SUCCEEDED(result)) && childList) - { - childList->GetLength(&count); - childList->Item(offset, getter_AddRefs(selectedNode)); - } - isFirst = PRBool(0==offset); - isLast = PRBool((count-1)==(PRUint32)offset); - } -// XXX: if isFirst && isLast, then we'll need to delete the node - // as well as the 1 child - - // build a transaction for deleting the appropriate data - // XXX: this has to come from rule section - if ((nsIEditor::eDeleteLeft==aAction) && (PR_TRUE==isFirst)) - { // we're backspacing from the beginning of the node. Delete the first thing to our left - nsCOMPtr priorNode; - result = GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode)); - if ((NS_SUCCEEDED(result)) && priorNode) - { // there is a priorNode, so delete it's last child (if text content, delete the last char.) - // if it has no children, delete it - nsCOMPtr priorNodeAsText; - priorNodeAsText = do_QueryInterface(priorNode); - if (priorNodeAsText) - { - PRUint32 length=0; - priorNodeAsText->GetLength(&length); - if (0AppendChild(txn); - } - } - else - { // XXX: can you have an empty text node? If so, what do you do? - printf("ERROR: found a text node with 0 characters\n"); - result = NS_ERROR_UNEXPECTED; - } - } - else - { // priorNode is not text, so tell it's parent to delete it - DeleteElementTxn *txn; - result = CreateTxnForDeleteElement(priorNode, &txn); - if (NS_SUCCEEDED(result)) { - aTxn->AppendChild(txn); - } - } - } - } - else if ((nsIEditor::eDeleteRight==aAction) && (PR_TRUE==isLast)) - { // we're deleting from the end of the node. Delete the first thing to our right - nsCOMPtr nextNode; - result = GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode)); - if ((NS_SUCCEEDED(result)) && nextNode) - { // there is a priorNode, so delete it's last child (if text content, delete the last char.) - // if it has no children, delete it - nsCOMPtr nextNodeAsText; - nextNodeAsText = do_QueryInterface(nextNode); - if (nextNodeAsText) - { - PRUint32 length=0; - nextNodeAsText->GetLength(&length); - if (0AppendChild(txn); - } - } - else - { // XXX: can you have an empty text node? If so, what do you do? - printf("ERROR: found a text node with 0 characters\n"); - result = NS_ERROR_UNEXPECTED; - } - } - else - { // nextNode is not text, so tell it's parent to delete it - DeleteElementTxn *txn; - result = CreateTxnForDeleteElement(nextNode, &txn); - if (NS_SUCCEEDED(result)) { - aTxn->AppendChild(txn); - } - } - } - } - else - { - if (nodeAsText) - { // we have text, so delete a char at the proper offset - if (nsIEditor::eDeleteLeft==aAction) { - offset --; - } - DeleteTextTxn *txn; - result = CreateTxnForDeleteText(nodeAsText, offset, 1, &txn); - if (NS_SUCCEEDED(result)) { - aTxn->AppendChild(txn); - } - } - else - { // we're deleting a node - DeleteElementTxn *txn; - result = CreateTxnForDeleteElement(selectedNode, &txn); - if (NS_SUCCEEDED(result)) { - aTxn->AppendChild(txn); - } - } - } - return result; -} - - -NS_IMETHODIMP -nsEditor::SplitNode(nsIDOMNode * aNode, - PRInt32 aOffset, - nsIDOMNode **aNewLeftNode) -{ - PRInt32 i; - nsIEditActionListener *listener; - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->WillSplitNode(aNode, aOffset); - } - } - - SplitElementTxn *txn; - nsresult result = CreateTxnForSplitNode(aNode, aOffset, &txn); - if (NS_SUCCEEDED(result)) - { - result = Do(txn); - if (NS_SUCCEEDED(result)) - { - result = txn->GetNewNode(aNewLeftNode); - NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode"); - } - } - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - { - nsIDOMNode *ptr = (aNewLeftNode) ? *aNewLeftNode : 0; - listener->DidSplitNode(aNode, aOffset, ptr, result); - } - } - } - - return result; -} NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode, PRUint32 aOffset, @@ -2625,43 +1960,6 @@ NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode, return result; } -NS_IMETHODIMP -nsEditor::JoinNodes(nsIDOMNode * aLeftNode, - nsIDOMNode * aRightNode, - nsIDOMNode * aParent) -{ - PRInt32 i; - nsIEditActionListener *listener; - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->WillJoinNodes(aLeftNode, aRightNode, aParent); - } - } - - JoinElementTxn *txn; - nsresult result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); - } - - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->DidJoinNodes(aLeftNode, aRightNode, aParent, result); - } - } - - return result; -} - NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, JoinElementTxn **aTxn) @@ -2680,6 +1978,12 @@ NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, // END nsEditor core implementation +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsEditor public static helper methods --- +#pragma mark - +#endif + // BEGIN nsEditor public static helper methods nsresult @@ -2849,6 +2153,7 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, return result; } + nsresult nsEditor::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset) { @@ -3615,213 +2920,6 @@ void nsEditor::HACKForceRedraw() #endif } - -NS_IMETHODIMP nsEditor::GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject) -{ - nsresult result = NS_ERROR_FAILURE; // we return an error unless we get the index - if( mPresShell != nsnull ) - { - if ((nsnull!=aNode)) - { // get the content interface - nsCOMPtr nodeAsContent( do_QueryInterface(aNode) ); - if (nodeAsContent) - { // get the frame from the content interface - //Note: frames are not ref counted, so don't use an nsCOMPtr - *aLayoutObject = nsnull; - result = mPresShell->GetLayoutObjectFor(nodeAsContent, aLayoutObject); - } - } - else { - result = NS_ERROR_NULL_POINTER; - } - } - return result; -} - -// -// The BeingComposition method is called from the Editor Composition event listeners. -// It caches the current text node and offset which is subsequently used for the -// created of IMETextTxn's. -// -NS_IMETHODIMP -nsEditor::BeginComposition(void) -{ -#ifdef DEBUG_tague - printf("nsEditor::StartComposition\n"); -#endif - nsresult result; - PRInt32 offset; - nsCOMPtr selection; - nsCOMPtr nodeAsText; - - result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - result = NS_ERROR_UNEXPECTED; - nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(result) && enumerator) - { - enumerator->First(); - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(result)) && (currentItem)) - { - result = NS_ERROR_UNEXPECTED; - nsCOMPtr range(do_QueryInterface(currentItem)); - if (range) - { - nsCOMPtr node; - result = range->GetStartParent(getter_AddRefs(node)); - if ((NS_SUCCEEDED(result)) && (node)) - { - nodeAsText = do_QueryInterface(node); - range->GetStartOffset(&offset); - if (!nodeAsText) { - result = NS_ERROR_EDITOR_NO_TEXTNODE; - } - } - } - } - else - { - result = NS_ERROR_EDITOR_NO_SELECTION; - } - } - } - - if (NS_SUCCEEDED(result) && nodeAsText) - { - // - // store the information needed to construct IME transactions for this composition - // - mIMETextNode = nodeAsText; - mIMETextOffset = offset; - mIMEBufferLength = 0; - } - - return result; -} - -NS_IMETHODIMP -nsEditor::EndComposition(void) -{ - nsresult result; - IMECommitTxn *commitTxn; - - // - // create the commit transaction..we can do it directly from the transaction mgr - // - result = TransactionFactory::GetNewTransaction(IMECommitTxn::GetCID(), (EditTxn**)&commitTxn); - if (NS_SUCCEEDED(result) && commitTxn!=nsnull) - { - commitTxn->Init(); - result = Do(commitTxn); - } - - /* reset the data we need to construct a transaction */ - mIMETextNode = do_QueryInterface(nsnull); - mIMETextOffset = 0; - mIMEBufferLength = 0; - - return result; -} - -NS_IMETHODIMP -nsEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply) -{ - nsCOMPtr caretP; - nsresult result = SetInputMethodText(aCompositionString,aTextRangeList); - mIMEBufferLength = aCompositionString.Length(); - - mPresShell->GetCaret(getter_AddRefs(caretP)); - caretP->GetWindowRelativeCoordinates(aReply->mCursorPosition,aReply->mCursorIsCollapsed); - - return result; -} - -NS_IMETHODIMP -nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) -{ - NS_NOTREACHED("This should never get called. Overridden by subclasses"); - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::StartLogging(nsIFileSpec *aLogFile) -{ -#ifdef ENABLE_JS_EDITOR_LOG - - mJSEditorLog = new nsJSEditorLog(this, aLogFile); - - if (!mJSEditorLog) - return NS_ERROR_OUT_OF_MEMORY; - - if (mTxnMgr) - { - mJSTxnLog = new nsJSTxnLog(mJSEditorLog); - - if (mJSTxnLog) - { - NS_ADDREF(mJSTxnLog); - mTxnMgr->AddListener(mJSTxnLog); - } - else - return NS_ERROR_OUT_OF_MEMORY; - } - -#endif // ENABLE_JS_EDITOR_LOG - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::StopLogging() -{ -#ifdef ENABLE_JS_EDITOR_LOG - - if (mTxnMgr && mJSTxnLog) - mTxnMgr->RemoveListener(mJSTxnLog); - - if (mJSTxnLog) - { - NS_RELEASE(mJSTxnLog); - mJSTxnLog = 0; - } - - if (mJSEditorLog) - { - delete mJSEditorLog; - mJSEditorLog = 0; - } - -#endif // ENABLE_JS_EDITOR_LOG - - return NS_OK; -} - -NS_IMETHODIMP -nsEditor::DebugDumpContent() const -{ - nsCOMPtrcontent; - nsCOMPtrnodeList; - nsAutoString bodyTag = "body"; - mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if (nodeList) - { - PRUint32 count; - nodeList->GetLength(&count); - NS_ASSERTION(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtrbodyNode; - nodeList->Item(0, getter_AddRefs(bodyNode)); - if (bodyNode) { - content = do_QueryInterface(bodyNode); - } - } - content->List(); - return NS_OK; -} - nsresult nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, @@ -3914,7 +3012,7 @@ nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode) //END nsEditor Private methods NS_IMETHODIMP -nsEditor::SetInputMethodText(const nsString& aStringToInsert,nsIPrivateTextRangeList *aTextRangeList) +nsEditor::SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRangeList *aTextRangeList) { IMETextTxn *txn; nsresult result; @@ -3964,58 +3062,9 @@ nsEditor::SetInputMethodText(const nsString& aStringToInsert,nsIPrivateTextRange return result; } -NS_IMETHODIMP -nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert, - nsIPrivateTextRangeList* aTextRangeList, - IMETextTxn ** aTxn) -{ - nsresult result; - - if (mIMETextNode==nsnull) - BeginComposition(); - - result = TransactionFactory::GetNewTransaction(IMETextTxn::GetCID(), (EditTxn **)aTxn); - if (nsnull!=*aTxn) { - result = (*aTxn)->Init(mIMETextNode,mIMETextOffset,mIMEBufferLength,aTextRangeList,aStringToInsert,mPresShell); - } - else { - result = NS_ERROR_OUT_OF_MEMORY; - } - return result; -} -NS_IMETHODIMP -nsEditor::CreateTxnForAddStyleSheet(nsICSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn) -{ - nsresult rv = TransactionFactory::GetNewTransaction(AddStyleSheetTxn::GetCID(), (EditTxn **)aTxn); - if (NS_FAILED(rv)) - return rv; - - if (! *aTxn) - return NS_ERROR_OUT_OF_MEMORY; - - return (*aTxn)->Init(this, aSheet); -} - - - -NS_IMETHODIMP -nsEditor::CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn) -{ - nsresult rv = TransactionFactory::GetNewTransaction(RemoveStyleSheetTxn::GetCID(), (EditTxn **)aTxn); - if (NS_FAILED(rv)) - return rv; - - if (! *aTxn) - return NS_ERROR_OUT_OF_MEMORY; - - return (*aTxn)->Init(this, aSheet); -} - - - -NS_IMETHODIMP nsEditor::DoInitialInputMethodInsert(const nsString & aStringToInsert,nsIPrivateTextRangeList* aTextRangeList) +NS_IMETHODIMP nsEditor::DoInitialInputMethodInsert(const nsString & aStringToInsert, nsIPrivateTextRangeList* aTextRangeList) { if (!mDoc) { return NS_ERROR_NOT_INITIALIZED; @@ -4734,40 +3783,481 @@ nsresult nsEditor::EndUpdateViewBatch() return NS_OK; } -#if 0 -nsresult nsEditor::OpenDialog(const nsString &url) -{ - // Get the content window as the parent for the dialog - //nsWebShellWindow that lets you retrieve this. GetContentWebShell -} + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- protected nsEditor methods --- +#pragma mark - #endif -/****************************************************************************** - * nsAutoSelectionReset - *****************************************************************************/ -nsAutoSelectionReset::nsAutoSelectionReset(nsIDOMSelection *aSel) -{ - mInitialized = PR_FALSE; - mSel = do_QueryInterface(aSel); - if (mSel) - { - mSel->GetAnchorNode(getter_AddRefs(mStartNode)); - mSel->GetAnchorOffset(&mStartOffset); - mSel->GetFocusNode(getter_AddRefs(mEndNode)); - mSel->GetFocusOffset(&mEndOffset); - if (mStartNode && mEndNode) - mInitialized = PR_TRUE; - } -} - -nsAutoSelectionReset::~nsAutoSelectionReset() +NS_IMETHODIMP +nsEditor::DeleteSelectionImpl(ESelectionCollapseDirection aAction) { - if (mSel && mInitialized) - { - // restore original selection - mSel->Collapse(mStartNode, mStartOffset); - mSel->Extend(mEndNode, mEndOffset); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->DeleteSelection(aAction); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult result; + + EditAggregateTxn *txn; + result = CreateTxnForDeleteSelection(aAction, &txn); + if (NS_SUCCEEDED(result)) { + result = Do(txn); } + + return result; +} + + +/* Non-interface, protected methods */ + +NS_IMETHODIMP +nsEditor::DoAfterDoTransaction(nsITransaction *aTxn) +{ + nsresult rv = NS_OK; + + PRBool isTransientTransaction; + rv = aTxn->GetIsTransient(&isTransientTransaction); + if (NS_FAILED(rv)) + return rv; + + if (!isTransientTransaction) + { + // we need to deal here with the case where the user saved after some + // edits, then undid one or more times. Then, the undo count is -ve, + // but we can't let a do take it back to zero. So we flip it up to + // a +ve number. + PRInt32 modCount; + GetDocModCount(modCount); + if (modCount < 0) + modCount = -modCount; + + rv = IncDocModCount(1); // don't count transient transactions + } + + return rv; +} + + +NS_IMETHODIMP +nsEditor::DoAfterUndoTransaction() +{ + nsresult rv = NS_OK; + + rv = IncDocModCount(-1); // all undoable transactions are non-transient + + return rv; +} + +NS_IMETHODIMP +nsEditor::DoAfterRedoTransaction() +{ + nsresult rv = NS_OK; + + rv = IncDocModCount(1); // all redoable transactions are non-transient + + return rv; +} + +NS_IMETHODIMP +nsEditor::DoAfterDocumentSave() +{ + // the mod count is reset by nsIDiskDocument. + NotifyDocumentListeners(eDocumentStateChanged); + return NS_OK; +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement, + const nsString& aAttribute, + const nsString& aValue, + ChangeAttributeTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (nsnull != aElement) + { + result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) { + result = (*aTxn)->Init(this, aElement, aAttribute, aValue, PR_FALSE); + } + } + return result; +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, + const nsString& aAttribute, + ChangeAttributeTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (nsnull != aElement) + { + result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) + { + nsAutoString value; + result = (*aTxn)->Init(this, aElement, aAttribute, value, PR_TRUE); + } + } + return result; +} + + +NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsString& aTag, + nsIDOMNode *aParent, + PRInt32 aPosition, + CreateElementTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (nsnull != aParent) + { + result = TransactionFactory::GetNewTransaction(CreateElementTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) { + result = (*aTxn)->Init(this, aTag, aParent, aPosition); + } + } + return result; +} + + +NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode, + nsIDOMNode * aParent, + PRInt32 aPosition, + InsertElementTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (aNode && aParent && aTxn) + { + result = TransactionFactory::GetNewTransaction(InsertElementTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) { + result = (*aTxn)->Init(aNode, aParent, aPosition, this); + } + } + return result; +} + +NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement, + DeleteElementTxn ** aTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (nsnull != aElement) + { + result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn); + if (NS_SUCCEEDED(result)) { + result = (*aTxn)->Init(aElement); + } + } + return result; +} + +NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) +{ + nsresult result = NS_ERROR_NULL_POINTER; + if (aAggTxn) + { + *aAggTxn = nsnull; + result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn**)aAggTxn); + + if (NS_FAILED(result) || !*aAggTxn) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // Set the name for the aggregate transaction + (*aAggTxn)->SetName(aTxnName); + + // Get current selection and setup txn to delete it, + // but only if selection exists (is not a collapsed "caret" state) + nsCOMPtr selection; + result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + PRBool collapsed; + result = selection->GetIsCollapsed(&collapsed); + if (NS_SUCCEEDED(result) && !collapsed) { + EditAggregateTxn *delSelTxn; + result = CreateTxnForDeleteSelection(eDoNothing, &delSelTxn); + if (NS_SUCCEEDED(result) && delSelTxn) { + (*aAggTxn)->AppendChild(delSelTxn); + } + } + } + } + return result; +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert, + nsIPrivateTextRangeList* aTextRangeList, + IMETextTxn ** aTxn) +{ + nsresult result; + + if (mIMETextNode==nsnull) + BeginComposition(); + + result = TransactionFactory::GetNewTransaction(IMETextTxn::GetCID(), (EditTxn **)aTxn); + if (nsnull!=*aTxn) { + result = (*aTxn)->Init(mIMETextNode,mIMETextOffset,mIMEBufferLength,aTextRangeList,aStringToInsert,mPresShell); + } + else { + result = NS_ERROR_OUT_OF_MEMORY; + } + return result; +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForAddStyleSheet(nsICSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn) +{ + nsresult rv = TransactionFactory::GetNewTransaction(AddStyleSheetTxn::GetCID(), (EditTxn **)aTxn); + if (NS_FAILED(rv)) + return rv; + + if (! *aTxn) + return NS_ERROR_OUT_OF_MEMORY; + + return (*aTxn)->Init(this, aSheet); +} + + + +NS_IMETHODIMP +nsEditor::CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn) +{ + nsresult rv = TransactionFactory::GetNewTransaction(RemoveStyleSheetTxn::GetCID(), (EditTxn **)aTxn); + if (NS_FAILED(rv)) + return rv; + + if (! *aTxn) + return NS_ERROR_OUT_OF_MEMORY; + + return (*aTxn)->Init(this, aSheet); +} + + +NS_IMETHODIMP +nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAction, + EditAggregateTxn ** aTxn) +{ + if (!aTxn) + return NS_ERROR_NULL_POINTER; + *aTxn = nsnull; + + nsresult result; + nsCOMPtr selection; + result = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + // Check whether the selection is collapsed and we should do nothing: + PRBool isCollapsed; + result = (selection->GetIsCollapsed(&isCollapsed)); + if (NS_SUCCEEDED(result) && isCollapsed && aAction == eDoNothing) + return NS_OK; + + // allocate the out-param transaction + result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn **)aTxn); + if (NS_FAILED(result)) { + return result; + } + + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next()) + { + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + range->GetIsCollapsed(&isCollapsed); + if (PR_FALSE==isCollapsed) + { + DeleteRangeTxn *txn; + result = TransactionFactory::GetNewTransaction(DeleteRangeTxn::GetCID(), (EditTxn **)&txn); + if ((NS_SUCCEEDED(result)) && (nsnull!=txn)) + { + txn->Init(this, range); + (*aTxn)->AppendChild(txn); + } + else + result = NS_ERROR_OUT_OF_MEMORY; + } + else + { // we have an insertion point. delete the thing in front of it or behind it, depending on aAction + result = CreateTxnForDeleteInsertionPoint(range, aAction, *aTxn); + } + } + } + } + } + + // if we didn't build the transaction correctly, destroy the out-param transaction so we don't leak it. + if (NS_FAILED(result)) + { + NS_IF_RELEASE(*aTxn); + } + + return result; +} + + +//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented +NS_IMETHODIMP +nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, + nsIEditor::ESelectionCollapseDirection + aAction, + EditAggregateTxn *aTxn) +{ + nsCOMPtr node; + PRBool isFirst; + PRBool isLast; + PRInt32 offset; + //PRInt32 length=1; + + // get the node and offset of the insertion point + nsresult result = aRange->GetStartParent(getter_AddRefs(node)); + if (NS_FAILED(result)) + return result; + result = aRange->GetStartOffset(&offset); + if (NS_FAILED(result)) + return result; + + // determine if the insertion point is at the beginning, middle, or end of the node + nsCOMPtr nodeAsText; + nsCOMPtr selectedNode; + nodeAsText = do_QueryInterface(node); + + if (nodeAsText) + { + PRUint32 count; + nodeAsText->GetLength(&count); + isFirst = PRBool(0==offset); + isLast = PRBool(count==(PRUint32)offset); + } + else + { + // get the child list and count + nsCOMPtrchildList; + PRUint32 count=0; + result = node->GetChildNodes(getter_AddRefs(childList)); + if ((NS_SUCCEEDED(result)) && childList) + { + childList->GetLength(&count); + childList->Item(offset, getter_AddRefs(selectedNode)); + } + isFirst = PRBool(0==offset); + isLast = PRBool((count-1)==(PRUint32)offset); + } +// XXX: if isFirst && isLast, then we'll need to delete the node + // as well as the 1 child + + // build a transaction for deleting the appropriate data + // XXX: this has to come from rule section + if ((eDeletePrevious==aAction) && (PR_TRUE==isFirst)) + { // we're backspacing from the beginning of the node. Delete the first thing to our left + nsCOMPtr priorNode; + result = GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode)); + if ((NS_SUCCEEDED(result)) && priorNode) + { // there is a priorNode, so delete it's last child (if text content, delete the last char.) + // if it has no children, delete it + nsCOMPtr priorNodeAsText; + priorNodeAsText = do_QueryInterface(priorNode); + if (priorNodeAsText) + { + PRUint32 length=0; + priorNodeAsText->GetLength(&length); + if (0AppendChild(txn); + } + } + else + { // XXX: can you have an empty text node? If so, what do you do? + printf("ERROR: found a text node with 0 characters\n"); + result = NS_ERROR_UNEXPECTED; + } + } + else + { // priorNode is not text, so tell it's parent to delete it + DeleteElementTxn *txn; + result = CreateTxnForDeleteElement(priorNode, &txn); + if (NS_SUCCEEDED(result)) { + aTxn->AppendChild(txn); + } + } + } + } + else if ((nsIEditor::eDeleteNext==aAction) && (PR_TRUE==isLast)) + { // we're deleting from the end of the node. Delete the first thing to our right + nsCOMPtr nextNode; + result = GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode)); + if ((NS_SUCCEEDED(result)) && nextNode) + { // there is a priorNode, so delete it's last child (if text content, delete the last char.) + // if it has no children, delete it + nsCOMPtr nextNodeAsText; + nextNodeAsText = do_QueryInterface(nextNode); + if (nextNodeAsText) + { + PRUint32 length=0; + nextNodeAsText->GetLength(&length); + if (0AppendChild(txn); + } + } + else + { // XXX: can you have an empty text node? If so, what do you do? + printf("ERROR: found a text node with 0 characters\n"); + result = NS_ERROR_UNEXPECTED; + } + } + else + { // nextNode is not text, so tell it's parent to delete it + DeleteElementTxn *txn; + result = CreateTxnForDeleteElement(nextNode, &txn); + if (NS_SUCCEEDED(result)) { + aTxn->AppendChild(txn); + } + } + } + } + else + { + if (nodeAsText) + { // we have text, so delete a char at the proper offset + if (nsIEditor::eDeletePrevious==aAction) { + offset --; + } + DeleteTextTxn *txn; + result = CreateTxnForDeleteText(nodeAsText, offset, 1, &txn); + if (NS_SUCCEEDED(result)) { + aTxn->AppendChild(txn); + } + } + else + { // we're deleting a node + DeleteElementTxn *txn; + result = CreateTxnForDeleteElement(selectedNode, &txn); + if (NS_SUCCEEDED(result)) { + aTxn->AppendChild(txn); + } + } + } + return result; } diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index 5cfb343a4b56..ca32802372d5 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -19,15 +19,20 @@ #ifndef __editor_h__ #define __editor_h__ +#include "nsCOMPtr.h" #include "prmon.h" + #include "nsIEditor.h" +#include "nsIEditorIMESupport.h" +#include "nsIEditorLogging.h" + #include "nsIDOMDocument.h" #include "nsIDOMSelection.h" #include "nsIDOMCharacterData.h" #include "nsIDOMEventListener.h" #include "nsIDOMRange.h" #include "nsIPrivateTextRange.h" -#include "nsCOMPtr.h" + #include "nsIStringBundle.h" #include "nsITransactionManager.h" #include "TransactionFactory.h" @@ -78,43 +83,10 @@ PRMonitor *GetEditorMonitor(); * manager, event interfaces. the idea for the event interfaces is to have them * delegate the actual commands to the editor independent of the XPFE implementation. */ -class nsEditor : public nsIEditor +class nsEditor : public nsIEditor, + public nsIEditorIMESupport, + public nsIEditorLogging { -private: - nsIPresShell *mPresShell; - nsIViewManager *mViewManager; - PRUint32 mUpdateCount; - nsCOMPtr mTxnMgr; - nsCOMPtr mEditProperty; - nsCOMPtr mLastStyleSheet; // is owning this dangerous? - - // - // data necessary to build IME transactions - // - nsCOMPtr mIMETextNode; - PRUint32 mIMETextOffset; - PRUint32 mIMEBufferLength; - - friend PRBool NSCanUnload(nsISupports* serviceMgr); - static PRInt32 gInstanceCount; - - nsVoidArray* mActionListeners; - nsCOMPtr mDocStateListeners; - nsCOMPtr mStringBundle; - - PRInt8 mDocDirtyState; // -1 = not initialized - -protected: - nsIDOMDocument * mDoc; - nsCOMPtr mDTD; - // Services are not nsCOMPtr friendly - nsIPref* mPrefs; - -#ifdef ENABLE_JS_EDITOR_LOG - nsJSEditorLog *mJSEditorLog; - nsJSTxnLog *mJSTxnLog; -#endif // ENABLE_JS_EDITOR_LOG - public: enum IterDirection @@ -134,77 +106,67 @@ public: * for someone to derive from the nsEditor later? I dont believe so. */ virtual ~nsEditor(); - -/*BEGIN nsIEditor for more details*/ //Interfaces for addref and release and queryinterface //NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsEditor NS_DECL_ISUPPORTS - NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell); - + /* ------------ nsIEditor methods -------------- */ + NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, PRUint32 aFlags); NS_IMETHOD PostCreate(); - + NS_IMETHOD GetFlags(PRUint32 *aFlags) = 0; + NS_IMETHOD SetFlags(PRUint32 aFlags) = 0; NS_IMETHOD GetDocument(nsIDOMDocument **aDoc); - - NS_IMETHOD GetBodyElement(nsIDOMElement **aElement); - NS_IMETHOD GetPresShell(nsIPresShell **aPS); - NS_IMETHOD GetSelection(nsIDOMSelection **aSelection); + + NS_IMETHOD EnableUndo(PRBool aEnable); + NS_IMETHOD Do(nsITransaction *aTxn); + NS_IMETHOD Undo(PRUint32 aCount); + NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo); + NS_IMETHOD Redo(PRUint32 aCount); + NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo); - NS_IMETHOD SetProperties(nsVoidArray *aPropList); + NS_IMETHOD BeginTransaction(); + NS_IMETHOD EndTransaction(); - NS_IMETHOD GetProperties(nsVoidArray *aPropList); + // file handling + NS_IMETHOD Save(); + NS_IMETHOD SaveAs(PRBool aSavingCopy); + NS_IMETHOD GetDocumentModified(PRBool *outDocModified); + // these are pure virtual in this base class + NS_IMETHOD Cut() = 0; + NS_IMETHOD Copy() = 0; + NS_IMETHOD Paste() = 0; + + NS_IMETHOD SelectAll(); + + NS_IMETHOD BeginningOfDocument(); + NS_IMETHOD EndOfDocument(); + + + /* Node and element manipulation */ NS_IMETHOD SetAttribute(nsIDOMElement * aElement, const nsString& aAttribute, const nsString& aValue); - + NS_IMETHOD GetAttributeValue(nsIDOMElement * aElement, const nsString& aAttribute, nsString& aResultValue, PRBool& aResultIsSet); - + NS_IMETHOD RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute); - //NOTE: Most callers are dealing with Nodes, - // but these objects must supports nsIDOMElement - NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); - NS_IMETHOD CreateNode(const nsString& aTag, nsIDOMNode * aParent, PRInt32 aPosition, nsIDOMNode ** aNewNode); - + NS_IMETHOD InsertNode(nsIDOMNode * aNode, nsIDOMNode * aParent, PRInt32 aPosition); - NS_IMETHOD InsertText(const nsString& aStringToInsert); - - NS_IMETHOD BeginComposition(void); - - NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList, nsTextEventReply *aReply); - - NS_IMETHOD EndComposition(void); - - NS_IMETHOD OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags); - NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags); - NS_IMETHOD DumpContentTree(); - - NS_IMETHOD DeleteNode(nsIDOMNode * aChild); - - NS_IMETHOD DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction); - - NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode); - - NS_IMETHOD SplitNode(nsIDOMNode * aExistingRightNode, PRInt32 aOffset, nsIDOMNode ** aNewLeftNode); @@ -213,69 +175,63 @@ public: nsIDOMNode * aRightNode, nsIDOMNode * aParent); - NS_IMETHOD InsertBreak(); + NS_IMETHOD DeleteNode(nsIDOMNode * aChild); - NS_IMETHOD EnableUndo(PRBool aEnable); - NS_IMETHOD Do(nsITransaction *aTxn); - - NS_IMETHOD Undo(PRUint32 aCount); - - NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo); - - NS_IMETHOD Redo(PRUint32 aCount); - - NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo); - - NS_IMETHOD BeginTransaction(); - - NS_IMETHOD EndTransaction(); - - NS_IMETHOD GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject); - - NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin); - - NS_IMETHOD SelectAll(); - - NS_IMETHOD BeginningOfDocument(); - - NS_IMETHOD EndOfDocument(); - - NS_IMETHOD Cut(); - - NS_IMETHOD Copy(); - - NS_IMETHOD Paste(); - - NS_IMETHOD PasteAsQuotation(); - - NS_IMETHOD InsertAsQuotation(const nsString& aQuotedText); - - NS_IMETHOD ApplyStyleSheet(const nsString& aURL); - NS_IMETHOD AddStyleSheet(nsICSSStyleSheet* aSheet); - NS_IMETHOD RemoveStyleSheet(nsICSSStyleSheet* aSheet); + /* output */ + NS_IMETHOD OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags); + + NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags); + /* Listeners */ NS_IMETHOD AddEditActionListener(nsIEditActionListener *aListener); - NS_IMETHOD RemoveEditActionListener(nsIEditActionListener *aListener); NS_IMETHOD AddDocumentStateListener(nsIDocumentStateListener *aListener); NS_IMETHOD RemoveDocumentStateListener(nsIDocumentStateListener *aListener); - NS_IMETHOD GetDocumentModified(PRBool *outDocModified); + + NS_IMETHOD DumpContentTree(); + NS_IMETHOD DebugDumpContent() const; NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); + /* ------------ nsIEditorIMESupport methods -------------- */ + + NS_IMETHOD BeginComposition(void); + NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply); + NS_IMETHOD EndComposition(void); + + /* ------------ nsIEditorLogging methods -------------- */ + NS_IMETHOD StartLogging(nsIFileSpec *aLogFile); NS_IMETHOD StopLogging(); -/*END nsIEditor interfaces*/ +public: - /* StyleSheet load callback */ - static void ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData); + NS_IMETHOD InsertTextImpl(const nsString& aStringToInsert); + NS_IMETHOD DeleteSelectionImpl(ESelectionCollapseDirection aAction); + -/*BEGIN private methods used by the implementations of the above functions*/ protected: + + + // why not use the one in nsHTMLDocument? + NS_IMETHOD GetBodyElement(nsIDOMElement **aElement); + + //NOTE: Most callers are dealing with Nodes, + // but these objects must supports nsIDOMElement + NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); + /* + NS_IMETHOD SetProperties(nsVoidArray *aPropList); + NS_IMETHOD GetProperties(nsVoidArray *aPropList); + */ + /** create a transaction for setting aAttribute to aValue on aElement */ NS_IMETHOD CreateTxnForSetAttribute(nsIDOMElement *aElement, @@ -308,6 +264,26 @@ protected: NS_IMETHOD CreateTxnForDeleteElement(nsIDOMNode * aElement, DeleteElementTxn ** aTxn); + + /** Create an aggregate transaction for deleting current selection + * Used by all methods that need to delete current selection, + * then insert something new to replace it + * @param nsString& aTxnName is the name of the aggregated transaction + * @param EditTxn **aAggTxn is the return location of the aggregate TXN, + * with the DeleteSelectionTxn as the first child ONLY + * if there was a selection to delete. + */ + NS_IMETHOD CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn); + + + NS_IMETHOD CreateTxnForDeleteSelection(ESelectionCollapseDirection aAction, + EditAggregateTxn ** aTxn); + + NS_IMETHOD CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, + ESelectionCollapseDirection aAction, + EditAggregateTxn *aTxn); + + /** create a transaction for inserting aStringToInsert into aTextNode * if aTextNode is null, the string is inserted at the current selection. */ @@ -326,15 +302,12 @@ protected: /** create a transaction for removing a style sheet */ NS_IMETHOD CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn); - - /* remove the old style sheet, and apply the supplied one */ - NS_IMETHOD ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet); /** insert aStringToInsert as the first text in the document */ NS_IMETHOD DoInitialInsert(const nsString & aStringToInsert); - NS_IMETHOD DoInitialInputMethodInsert(const nsString& aStringToInsert,nsIPrivateTextRangeList* aTextRangeList); + NS_IMETHOD DoInitialInputMethodInsert(const nsString & aStringToInsert, nsIPrivateTextRangeList* aTextRangeList); NS_IMETHOD DeleteText(nsIDOMCharacterData *aElement, @@ -345,14 +318,7 @@ protected: PRUint32 aOffset, PRUint32 aLength, DeleteTextTxn **aTxn); - - NS_IMETHOD CreateTxnForDeleteSelection(nsIEditor::ECollapsedSelectionAction aAction, - EditAggregateTxn ** aTxn); - - NS_IMETHOD CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, - nsIEditor::ECollapsedSelectionAction aAction, - EditAggregateTxn *aTxn); - + NS_IMETHOD CreateTxnForSplitNode(nsIDOMNode *aNode, PRUint32 aOffset, SplitElementTxn **aTxn); @@ -361,19 +327,8 @@ protected: nsIDOMNode *aRightNode, JoinElementTxn **aTxn); - /** Create an aggregate transaction for deleting current selection - * Used by all methods that need to delete current selection, - * then insert something new to replace it - * @param nsString& aTxnName is the name of the aggregated transaction - * @param EditTxn **aAggTxn is the return location of the aggregate TXN, - * with the DeleteSelectionTxn as the first child ONLY - * if there was a selection to delete. - */ - NS_IMETHOD CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn); - NS_IMETHOD DebugDumpContent() const; - - NS_IMETHOD SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRangeList* aTextRangeList); + NS_IMETHOD SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRangeList *aTextRangeList); // called each time we modify the document. Increments the mod // count of the doc. @@ -415,9 +370,16 @@ protected: // document after a change via the DOM - gpk 2/13/99 void HACKForceRedraw(void); - NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode); +// file handling utils + + NS_IMETHOD SaveDocument(PRBool saveAs, PRBool saveCopy); + + NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin); public: + + static nsString& GetTextNodeTag(); + /** * SplitNode() creates a new node identical to an existing node, and split the contents between the two nodes * @param aExistingRightNode the node to split. It will become the new node's next sibling. @@ -626,39 +588,45 @@ public: nsresult BeginUpdateViewBatch(void); nsresult EndUpdateViewBatch(void); -}; -class nsAutoEditBatch -{ - private: - nsCOMPtr mEd; - public: - nsAutoEditBatch( nsIEditor *aEd) : mEd(do_QueryInterface(aEd)) - { if (mEd) mEd->BeginTransaction(); } - ~nsAutoEditBatch() { if (mEd) mEd->EndTransaction(); } -}; +protected: -class nsAutoSelectionReset -{ - private: - /** ref-counted reference to the selection that we are supposed to restore */ - nsCOMPtr mSel; + PRUint32 mFlags; // behavior flags. See nsIHTMLEditor.h for the flags we use. + + nsIPresShell *mPresShell; + nsIViewManager *mViewManager; + PRUint32 mUpdateCount; + nsCOMPtr mTxnMgr; + nsCOMPtr mEditProperty; + nsCOMPtr mLastStyleSheet; // is owning this dangerous? - /** PR_TRUE if this instance initialized itself correctly */ - PRBool mInitialized; + // + // data necessary to build IME transactions + // + nsCOMPtr mIMETextNode; + PRUint32 mIMETextOffset; + PRUint32 mIMEBufferLength; - nsCOMPtr mStartNode; - nsCOMPtr mEndNode; - PRInt32 mStartOffset; - PRInt32 mEndOffset; + nsVoidArray* mActionListeners; + nsCOMPtr mDocStateListeners; + nsCOMPtr mStringBundle; - public: - /** constructor responsible for remembering all state needed to restore aSel */ - nsAutoSelectionReset(nsIDOMSelection *aSel); - - /** destructor restores mSel to its former state */ - ~nsAutoSelectionReset(); + PRInt8 mDocDirtyState; // -1 = not initialized + + nsIDOMDocument * mDoc; + nsCOMPtr mDTD; + // Services are not nsCOMPtr friendly + nsIPref* mPrefs; + +#ifdef ENABLE_JS_EDITOR_LOG + nsJSEditorLog *mJSEditorLog; + nsJSTxnLog *mJSTxnLog; +#endif // ENABLE_JS_EDITOR_LOG + + static PRInt32 gInstanceCount; + + friend PRBool NSCanUnload(nsISupports* serviceMgr); }; diff --git a/editor/libeditor/html/TextEditorTest.cpp b/editor/libeditor/html/TextEditorTest.cpp index c234063a0ae1..91f89e6cb563 100644 --- a/editor/libeditor/html/TextEditorTest.cpp +++ b/editor/libeditor/html/TextEditorTest.cpp @@ -17,7 +17,7 @@ */ #include -#include "nsITextEditor.h" + #include "nsIEditor.h" #include "TextEditorTest.h" #include "nsIDOMSelection.h" @@ -43,7 +43,7 @@ TextEditorTest::~TextEditorTest() printf("destroyed a TextEditorTest\n"); } -void TextEditorTest::Run(nsITextEditor *aEditor, PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) +void TextEditorTest::Run(nsIEditor *aEditor, PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) { if (!aEditor) return; mTextEditor = do_QueryInterface(aEditor); @@ -98,9 +98,9 @@ nsresult TextEditorTest::RunUnitTest(PRInt32 *outNumTests, PRInt32 *outNumTestsF nsresult TextEditorTest::InitDoc() { - nsresult result = mTextEditor->SelectAll(); + nsresult result = mEditor->SelectAll(); TEST_RESULT(result); - result = mTextEditor->DeleteSelection(nsIEditor::eDeleteRight); + result = mEditor->DeleteSelection(nsIEditor::eDeleteNext); TEST_RESULT(result); return result; } @@ -165,14 +165,14 @@ nsresult TextEditorTest::TestTextProperties() PRBool any = PR_FALSE; PRBool all = PR_FALSE; PRBool first=PR_FALSE; - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_FALSE==first, "first should be false"); NS_ASSERTION(PR_FALSE==any, "any should be false"); NS_ASSERTION(PR_FALSE==all, "all should be false"); - result = mTextEditor->SetTextProperty(nsIEditProperty::b, nsnull, nsnull); + result = mTextEditor->SetInlineProperty(nsIEditProperty::b, nsnull, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); @@ -181,9 +181,9 @@ nsresult TextEditorTest::TestTextProperties() // remove the bold we just set printf("set the whole first text node to not bold\n"); - result = mTextEditor->RemoveTextProperty(nsIEditProperty::b, nsnull); + result = mTextEditor->RemoveInlineProperty(nsIEditProperty::b, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_FALSE==first, "first should be false"); NS_ASSERTION(PR_FALSE==any, "any should be false"); @@ -194,23 +194,23 @@ nsresult TextEditorTest::TestTextProperties() printf("set the first text node (1, length-1) to bold and italic, and (2, length-1) to underline.\n"); selection->Collapse(textNode, 1); selection->Extend(textNode, length-1); - result = mTextEditor->SetTextProperty(nsIEditProperty::b, nsnull, nsnull); + result = mTextEditor->SetInlineProperty(nsIEditProperty::b, nsnull, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); NS_ASSERTION(PR_TRUE==all, "all should be true"); mEditor->DebugDumpContent(); // make all that same text italic - result = mTextEditor->SetTextProperty(nsIEditProperty::i, nsnull, nsnull); + result = mTextEditor->SetInlineProperty(nsIEditProperty::i, nsnull, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::i, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::i, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); NS_ASSERTION(PR_TRUE==all, "all should be true"); - result = mTextEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); @@ -231,9 +231,9 @@ nsresult TextEditorTest::TestTextProperties() NS_ASSERTION(length==249, "wrong text node"); selection->Collapse(textNode, 1); selection->Extend(textNode, length-2); - result = mTextEditor->SetTextProperty(nsIEditProperty::u, nsnull, nsnull); + result = mTextEditor->SetInlineProperty(nsIEditProperty::u, nsnull, nsnull); TEST_RESULT(result); - result = mTextEditor->GetTextProperty(nsIEditProperty::u, nsnull, nsnull, first, any, all); + result = mTextEditor->GetInlineProperty(nsIEditProperty::u, nsnull, nsnull, first, any, all); TEST_RESULT(result); NS_ASSERTION(PR_TRUE==first, "first should be true"); NS_ASSERTION(PR_TRUE==any, "any should be true"); diff --git a/editor/libeditor/html/TextEditorTest.h b/editor/libeditor/html/TextEditorTest.h index 10bc575b8ed7..5d29022574b5 100644 --- a/editor/libeditor/html/TextEditorTest.h +++ b/editor/libeditor/html/TextEditorTest.h @@ -22,14 +22,14 @@ #ifdef NS_DEBUG #include "nsCOMPtr.h" -#include "nsITextEditor.h" #include "nsIEditor.h" +#include "nsIHTMLEditor.h" class TextEditorTest { public: - void Run(nsITextEditor *aEditor, PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); + void Run(nsIEditor *aEditor, PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); TextEditorTest(); ~TextEditorTest(); @@ -44,7 +44,7 @@ protected: nsresult TestTextProperties(); - nsCOMPtr mTextEditor; + nsCOMPtr mTextEditor; nsCOMPtr mEditor; }; diff --git a/editor/libeditor/html/TypeInState.h b/editor/libeditor/html/TypeInState.h index 2bea71928adc..9ae84010933b 100644 --- a/editor/libeditor/html/TypeInState.h +++ b/editor/libeditor/html/TypeInState.h @@ -16,8 +16,8 @@ * Reserved. */ -#ifndef ChangeAttributeTxn_h__ -#define ChangeAttributeTxn_h__ +#ifndef TypeInState_h__ +#define TypeInState_h__ #include "nsIDOMSelectionListener.h" #include "nsIEditProperty.h" @@ -228,4 +228,5 @@ inline void TypeInState::SetPropValue(PRUint32 aProp, const nsString &aValue) } } -#endif +#endif // TypeInState_h__ + diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp index 20641a71e39f..35942e8f9574 100644 --- a/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/editor/libeditor/html/nsHTMLEditRules.cpp @@ -17,11 +17,13 @@ */ #include "nsHTMLEditRules.h" + #include "nsEditor.h" #include "nsHTMLEditor.h" -#include "nsTextEditor.h" + #include "PlaceholderTxn.h" #include "InsertTextTxn.h" + #include "nsIContent.h" #include "nsIContentIterator.h" #include "nsIDOMNode.h" @@ -36,7 +38,7 @@ #include "nsIPresShell.h" #include "nsLayoutCID.h" -class nsIFrame; +#include "nsEditorUtils.h" //const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE"; //const static char* kMOZEditorBogusNodeValue="TRUE"; @@ -56,7 +58,8 @@ enum * Constructor/Destructor ********************************************************/ -nsHTMLEditRules::nsHTMLEditRules() +nsHTMLEditRules::nsHTMLEditRules(PRUint32 aFlags) +: nsTextEditRules(aFlags) { } @@ -285,7 +288,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel) nsresult -nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECollapsedSelectionAction aAction, PRBool *aCancel) +nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction, PRBool *aCancel) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param @@ -315,7 +318,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECo if (NS_FAILED(res)) return res; // at beginning of text node and backspaced? - if (!offset && (aAction == nsIEditor::eDeleteLeft)) + if (!offset && (aAction == nsIEditor::eDeletePrevious)) { nsCOMPtr priorNode; res = mEditor->GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode)); @@ -368,7 +371,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECo // at end of text node and deleted? if ((offset == (PRInt32)strLength) - && (aAction == nsIEditor::eDeleteRight)) + && (aAction == nsIEditor::eDeleteNext)) { nsCOMPtr nextNode; res = mEditor->GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode)); @@ -480,7 +483,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECo { // first delete the selection *aCancel = PR_TRUE; - res = mEditor->nsEditor::DeleteSelection(aAction); + res = mEditor->DeleteSelectionImpl(aAction); if (NS_FAILED(res)) return res; // then join para's, insert break res = mEditor->JoinNodeDeep(leftParent,rightParent,aSelection); @@ -493,7 +496,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECo { // first delete the selection *aCancel = PR_TRUE; - res = mEditor->nsEditor::DeleteSelection(aAction); + res = mEditor->DeleteSelectionImpl(aAction); if (NS_FAILED(res)) return res; // join blocks res = mEditor->JoinNodeDeep(leftParent,rightParent,aSelection); @@ -1284,7 +1287,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt } if (node) - offset++; // since this is going to be used for a range _endpoint_, we want to be after the node + offset++; // since this is going to be used for a range _endpoint_, we want to be after the node else node = parent; @@ -1859,11 +1862,3 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, } - - - - - - - - diff --git a/editor/libeditor/html/nsHTMLEditRules.h b/editor/libeditor/html/nsHTMLEditRules.h index a975859c3b96..e37c8ce57cc4 100644 --- a/editor/libeditor/html/nsHTMLEditRules.h +++ b/editor/libeditor/html/nsHTMLEditRules.h @@ -30,8 +30,8 @@ class nsHTMLEditRules : public nsTextEditRules { public: - nsHTMLEditRules(); - virtual ~nsHTMLEditRules(); + nsHTMLEditRules(PRUint32 aFlags); + virtual ~nsHTMLEditRules(); // nsEditRules methods NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel); @@ -61,7 +61,7 @@ protected: TypeInState typeInState, PRInt32 aMaxLength); nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel); - nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ECollapsedSelectionAction aAction, PRBool *aCancel); + nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction, PRBool *aCancel); nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel); nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel); nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel); diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp index 872746ba674f..5d9246a273a2 100644 --- a/editor/libeditor/html/nsHTMLEditor.cpp +++ b/editor/libeditor/html/nsHTMLEditor.cpp @@ -16,11 +16,12 @@ * Reserved. */ -#include "nsTextEditor.h" + #include "nsHTMLEditor.h" #include "nsHTMLEditRules.h" + #include "nsEditorEventListeners.h" -#include "nsInsertHTMLTxn.h" + #include "nsIDOMText.h" #include "nsIDOMNodeList.h" #include "nsIDOMDocument.h" @@ -31,6 +32,16 @@ #include "nsIDOMSelection.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMHTMLImageElement.h" + +#include "nsICSSLoader.h" +#include "nsICSSStyleSheet.h" +#include "nsIHTMLContentContainer.h" +#include "nsIStyleSet.h" +#include "nsIDocumentObserver.h" +#include "nsIDocumentStateListener.h" + +#include "nsIStyleContext.h" + #include "nsIEnumerator.h" #include "nsIContent.h" #include "nsIContentIterator.h" @@ -49,12 +60,27 @@ #include "nsIDOMDocumentFragment.h" #include "nsIPresShell.h" #include "nsIImage.h" +#include "nsAOLCiter.h" +#include "nsInternetCiter.h" + +// netwerk +#include "nsIURI.h" +#include "nsNeckoUtil.h" // Drag & Drop, Clipboard #include "nsWidgetsCID.h" #include "nsIClipboard.h" #include "nsITransferable.h" +// Transactionas +#include "PlaceholderTxn.h" +#include "nsStyleSheetTxns.h" +#include "nsInsertHTMLTxn.h" + +// Misc +#include "TextEditorTest.h" +#include "nsEditorUtils.h" + #include "prprf.h" const unsigned char nbsp = 160; @@ -69,12 +95,11 @@ const unsigned char nbsp = 160; { 0xa6cf9107, 0x15b3, 0x11d2, { 0x93, 0x2e, 0x0, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } } static NS_DEFINE_CID(kCNavDTDCID, NS_CNAVDTD_CID); -static NS_DEFINE_CID(kEditorCID, NS_EDITOR_CID); -static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); // Drag & Drop, Clipboard Support static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); @@ -125,7 +150,14 @@ static PRBool IsNamedAnchorNode(nsIDOMNode *aNode) return PR_FALSE; } + nsHTMLEditor::nsHTMLEditor() +: nsEditor() +, mTypeInState(nsnull) +, mRules(nsnull) +, mIsComposing(PR_FALSE) +, mMaxTextLength(-1) +, mWrapColumn(0) { // Done in nsEditor // NS_INIT_REFCNT(); @@ -134,562 +166,694 @@ nsHTMLEditor::nsHTMLEditor() nsHTMLEditor::~nsHTMLEditor() { //the autopointers will clear themselves up. + //but we need to also remove the listeners or we have a leak + nsCOMPtrselection; + nsresult result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) + { + nsCOMPtrlistener; + listener = do_QueryInterface(mTypeInState); + if (listener) { + selection->RemoveSelectionListener(listener); + } + } + nsCOMPtr doc; + nsEditor::GetDocument(getter_AddRefs(doc)); + if (doc) + { + nsCOMPtr erP = do_QueryInterface(doc, &result); + if (NS_SUCCEEDED(result) && erP) + { + if (mKeyListenerP) { + erP->RemoveEventListenerByIID(mKeyListenerP, nsIDOMKeyListener::GetIID()); + } + if (mMouseListenerP) { + erP->RemoveEventListenerByIID(mMouseListenerP, nsIDOMMouseListener::GetIID()); + } + if (mTextListenerP) { + erP->RemoveEventListenerByIID(mTextListenerP, nsIDOMTextListener::GetIID()); + } + if (mCompositionListenerP) { + erP->RemoveEventListenerByIID(mCompositionListenerP, nsIDOMCompositionListener::GetIID()); + } + if (mFocusListenerP) { + erP->RemoveEventListenerByIID(mFocusListenerP, nsIDOMFocusListener::GetIID()); + } + if (mDragListenerP) { + erP->RemoveEventListenerByIID(mDragListenerP, nsIDOMDragListener::GetIID()); + } + } + else + NS_NOTREACHED("~nsTextEditor"); + } + + // deleting a null pointer is safe + delete mRules; + + NS_IF_RELEASE(mTypeInState); } -// Adds appropriate AddRef, Release, and QueryInterface methods for derived class -//NS_IMPL_ISUPPORTS_INHERITED(nsHTMLEditor, nsTextEditor, nsIHTMLEditor) +NS_IMPL_ADDREF_INHERITED(nsHTMLEditor, nsEditor) +NS_IMPL_RELEASE_INHERITED(nsHTMLEditor, nsEditor) -//NS_IMPL_ADDREF_INHERITED(Class, Super) -NS_IMETHODIMP_(nsrefcnt) nsHTMLEditor::AddRef(void) -{ - return nsTextEditor::AddRef(); -} -//NS_IMPL_RELEASE_INHERITED(Class, Super) -NS_IMETHODIMP_(nsrefcnt) nsHTMLEditor::Release(void) -{ - return nsTextEditor::Release(); -} - -//NS_IMPL_QUERY_INTERFACE_INHERITED(Class, Super, AdditionalInterface) NS_IMETHODIMP nsHTMLEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) { - if (!aInstancePtr) return NS_ERROR_NULL_POINTER; + if (!aInstancePtr) + return NS_ERROR_NULL_POINTER; + *aInstancePtr = nsnull; + if (aIID.Equals(nsIHTMLEditor::GetIID())) { *aInstancePtr = NS_STATIC_CAST(nsIHTMLEditor*, this); NS_ADDREF_THIS(); return NS_OK; } - return nsTextEditor::QueryInterface(aIID, aInstancePtr); + if (aIID.Equals(nsIEditorMailSupport::GetIID())) { + *aInstancePtr = NS_STATIC_CAST(nsIEditorMailSupport*, this); + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(nsITableEditor::GetIID())) { + *aInstancePtr = NS_STATIC_CAST(nsITableEditor*, this); + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(nsIEditorStyleSheets::GetIID())) { + *aInstancePtr = NS_STATIC_CAST(nsIEditorStyleSheets*, this); + NS_ADDREF_THIS(); + return NS_OK; + } + + return nsEditor::QueryInterface(aIID, aInstancePtr); } NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, - nsIPresShell *aPresShell) + nsIPresShell *aPresShell, PRUint32 aFlags) { - NS_PRECONDITION(nsnull!=aDoc && nsnull!=aPresShell, "bad arg"); - nsresult res=NS_ERROR_NULL_POINTER; - if ((nsnull!=aDoc) && (nsnull!=aPresShell)) + NS_PRECONDITION(aDoc && aPresShell, "bad arg"); + if (!aDoc || !aPresShell) + return NS_ERROR_NULL_POINTER; + + nsresult result = NS_ERROR_NULL_POINTER; + // Init the base editor + result = nsEditor::Init(aDoc, aPresShell, aFlags); + if (NS_OK != result) { return result; } + + // init the type-in state + mTypeInState = new TypeInState(); + if (!mTypeInState) {return NS_ERROR_NULL_POINTER;} + NS_ADDREF(mTypeInState); + + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_OK != result) { return result; } + if (selection) { - res = nsTextEditor::Init(aDoc, aPresShell); - if (NS_SUCCEEDED(res)) - { - // Set up a DTD XXX XXX - // HACK: This should have happened in a document specific way - // in nsEditor::Init(), but we dont' have a way to do that yet - res = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull, - nsIDTD::GetIID(), getter_AddRefs(mDTD)); - if (!mDTD) res = NS_ERROR_FAILURE; + nsCOMPtrlistener; + listener = do_QueryInterface(mTypeInState); + if (listener) { + selection->AddSelectionListener(listener); } } - return res; + + // Init the rules system + InitRules(); + + // get a key listener + result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this); + if (NS_OK != result) { + HandleEventListenerError(); + return result; + } + + // get a mouse listener + result = NS_NewEditorMouseListener(getter_AddRefs(mMouseListenerP), this); + if (NS_OK != result) { + HandleEventListenerError(); + return result; + } + + // get a text listener + result = NS_NewEditorTextListener(getter_AddRefs(mTextListenerP),this); + if (NS_OK !=result) + { +#ifdef DEBUG_TAGUE +printf("nsTextEditor.cpp: failed to get TextEvent Listener\n"); +#endif + HandleEventListenerError(); + return result; + } + + // get a composition listener + result = NS_NewEditorCompositionListener(getter_AddRefs(mCompositionListenerP),this); + if (NS_OK!=result) + { +#ifdef DEBUG_TAGUE +printf("nsTextEditor.cpp: failed to get TextEvent Listener\n"); +#endif + HandleEventListenerError(); + return result; + } + + // get a drag listener + result = NS_NewEditorDragListener(getter_AddRefs(mDragListenerP), this); + if (NS_OK != result) + { + HandleEventListenerError(); + //return result; // XXX: why is this return commented out? + } + + // get a focus listener + result = NS_NewEditorFocusListener(getter_AddRefs(mFocusListenerP), this); + if (NS_OK != result) + { + HandleEventListenerError(); + return result; + } + + // get the DOM event receiver + nsCOMPtr erP; + result = aDoc->QueryInterface(nsIDOMEventReceiver::GetIID(), getter_AddRefs(erP)); + if (!NS_SUCCEEDED(result)) + { + HandleEventListenerError(); + return result; + } + + // register the event listeners with the DOM event reveiver + result = erP->AddEventListenerByIID(mKeyListenerP, nsIDOMKeyListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register key listener"); + result = erP->AddEventListenerByIID(mMouseListenerP, nsIDOMMouseListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register mouse listener"); + result = erP->AddEventListenerByIID(mFocusListenerP, nsIDOMFocusListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register focus listener"); + result = erP->AddEventListenerByIID(mTextListenerP, nsIDOMTextListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register text listener"); + result = erP->AddEventListenerByIID(mCompositionListenerP, nsIDOMCompositionListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register composition listener"); + result = erP->AddEventListenerByIID(mDragListenerP, nsIDOMDragListener::GetIID()); + NS_ASSERTION(NS_SUCCEEDED(result), "failed to register drag listener"); + + result = NS_OK; + EnableUndo(PR_TRUE); + + // Set up a DTD XXX XXX + // HACK: This should have happened in a document specific way + // in nsEditor::Init(), but we dont' have a way to do that yet + result = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull, + nsIDTD::GetIID(), getter_AddRefs(mDTD)); + if (!mDTD) result = NS_ERROR_FAILURE; + + return result; } -void nsHTMLEditor::InitRules() +NS_IMETHODIMP +nsHTMLEditor::GetFlags(PRUint32 *aFlags) +{ + if (!mRules || !aFlags) { return NS_ERROR_NULL_POINTER; } + return mRules->GetFlags(aFlags); +} + + +NS_IMETHODIMP +nsHTMLEditor::SetFlags(PRUint32 aFlags) +{ + if (!mRules) { return NS_ERROR_NULL_POINTER; } + return mRules->SetFlags(aFlags); +} + + +void nsHTMLEditor::InitRules() { // instantiate the rules for this text editor // XXX: we should be told which set of rules to instantiate - mRules = new nsHTMLEditRules(); + if (mFlags & eEditorPlaintextMask) + mRules = new nsTextEditRules(mFlags); + else + mRules = new nsHTMLEditRules(mFlags); + mRules->Init(this); } -NS_IMETHODIMP nsHTMLEditor::SetTextProperty(nsIAtom *aProperty, +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIHTMLEditor methods --- +#pragma mark - +#endif + + +NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue) { - return nsTextEditor::SetTextProperty(aProperty, aAttribute, aValue); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SetTextProperty(aProperty, aAttribute, aValue); +#endif // ENABLE_JS_EDITOR_LOG + + if (!aProperty) { return NS_ERROR_NULL_POINTER; } + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("---------- start nsTextEditor::SetTextProperty %s ----------\n", propCString); } + delete [] propCString; + } + + nsresult result=NS_ERROR_NOT_INITIALIZED; + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + PRBool cancel; + nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty); + result = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) + { + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (PR_TRUE==isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, aValue); + } + else + { + // set the text property for all selected ranges + nsEditor::BeginTransaction(); + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + nsCOMPtrcommonParent; + result = range->GetCommonParent(getter_AddRefs(commonParent)); + if ((NS_SUCCEEDED(result)) && commonParent) + { + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr startParent; nsCOMPtr endParent; + range->GetStartParent(getter_AddRefs(startParent)); + range->GetEndParent(getter_AddRefs(endParent)); + PRBool startIsText = IsTextNode(startParent); + PRBool endIsText = IsTextNode(endParent); + if ((PR_TRUE==startIsText) && (PR_TRUE==endIsText) && + (startParent.get()==endParent.get())) + { // the range is entirely contained within a single text node + // commonParent==aStartParent, so get the "real" parent of the selection + startParent->GetParentNode(getter_AddRefs(commonParent)); + result = SetTextPropertiesForNode(startParent, commonParent, + startOffset, endOffset, + aProperty, aAttribute, aValue); + } + else + { + nsCOMPtr startGrandParent; + startParent->GetParentNode(getter_AddRefs(startGrandParent)); + nsCOMPtr endGrandParent; + endParent->GetParentNode(getter_AddRefs(endGrandParent)); + if (NS_SUCCEEDED(result)) + { + PRBool canCollapseStyleNode = PR_FALSE; + if ((PR_TRUE==startIsText) && (PR_TRUE==endIsText) && + endGrandParent.get()==startGrandParent.get()) + { + result = IntermediateNodesAreInline(range, startParent, startOffset, + endParent, endOffset, + canCollapseStyleNode); + } + if (NS_SUCCEEDED(result)) + { + if (PR_TRUE==canCollapseStyleNode) + { // the range is between 2 nodes that have a common (immediate) grandparent, + // and any intermediate nodes are just inline style nodes + result = SetTextPropertiesForNodesWithSameParent(startParent,startOffset, + endParent, endOffset, + commonParent, + aProperty, aAttribute, aValue); + } + else + { // the range is between 2 nodes that have no simple relationship + result = SetTextPropertiesForNodeWithDifferentParents(range, + startParent,startOffset, + endParent, endOffset, + commonParent, + aProperty, aAttribute, aValue); + } + } + } + } + if (NS_SUCCEEDED(result)) + { // compute a range for the selection + // don't want to actually do anything with selection, because + // we are still iterating through it. Just want to create and remember + // an nsIDOMRange, and later add the range to the selection after clearing it. + // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism + // for setting a compound selection yet. + } + } + } + } + nsEditor::EndTransaction(); + } + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + } + if (gNoisy) {DebugDumpContent(); } // DEBUG + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("---------- end nsTextEditor::SetTextProperty %s ----------\n", propCString); } + delete [] propCString; + } + return result; } -NS_IMETHODIMP nsHTMLEditor::GetTextProperty(nsIAtom *aProperty, +NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue, PRBool &aFirst, PRBool &aAny, PRBool &aAll) { - return nsTextEditor::GetTextProperty(aProperty, aAttribute, aValue, aFirst, aAny, aAll); -} - -NS_IMETHODIMP nsHTMLEditor::RemoveTextProperty(nsIAtom *aProperty, const nsString *aAttribute) -{ - return nsTextEditor::RemoveTextProperty(aProperty, aAttribute); -} - -NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction) -{ - return nsTextEditor::DeleteSelection(aAction); -} - -NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) -{ - return nsTextEditor::InsertText(aStringToInsert); -} - -NS_IMETHODIMP nsHTMLEditor::SetBackgroundColor(const nsString& aColor) -{ -// nsresult result; - NS_ASSERTION(mDoc, "Missing Editor DOM Document"); - - // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - // For initial testing, just set the background on the BODY tag (the document's background) - -// Do this only if setting a table or cell background -// It will be called in nsTextEditor::SetBackgroundColor for the page background -#if 0 //def ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->SetBackgroundColor(aColor); -#endif // ENABLE_JS_EDITOR_LOG - - NS_ASSERTION(mDoc, "Missing Editor DOM Document"); - - // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - // For initial testing, just set the background on the BODY tag (the document's background) - - return nsTextEditor::SetBackgroundColor(aColor); -} - -NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsString& aAttribute, const nsString& aValue) -{ - -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->SetBodyAttribute(aAttribute, aValue); -#endif // ENABLE_JS_EDITOR_LOG - - nsresult res; - // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - - NS_ASSERTION(mDoc, "Missing Editor DOM Document"); - - // Set the background color attribute on the body tag - nsCOMPtr bodyElement; - - res = nsEditor::GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_SUCCEEDED(res) && bodyElement) - { - // Use the editor's method which goes through the transaction system - SetAttribute(bodyElement, aAttribute, aValue); + if (!aProperty) + return NS_ERROR_NULL_POINTER; +/* + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("nsTextEditor::GetTextProperty %s\n", propCString); } + delete [] propCString; } - return res; +*/ + nsresult result=NS_ERROR_NOT_INITIALIZED; + aAny=PR_FALSE; + aAll=PR_TRUE; + aFirst=PR_FALSE; + PRBool first=PR_TRUE; + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + // XXX: should be a while loop, to get each separate range + if ((NS_SUCCEEDED(result)) && currentItem) + { + PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag + nsCOMPtr range( do_QueryInterface(currentItem) ); + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), + getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + iter->Init(range); + nsCOMPtr content; + result = iter->CurrentNode(getter_AddRefs(content)); + while (NS_COMFALSE == iter->IsDone()) + { + //if (gNoisy) { printf(" checking node %p\n", content.get()); } + nsCOMPtrtext; + text = do_QueryInterface(content); + PRBool skipNode = PR_FALSE; + if (text) + { + if (PR_FALSE==isCollapsed && PR_TRUE==first && PR_TRUE==firstNodeInRange) + { + firstNodeInRange = PR_FALSE; + PRInt32 startOffset; + range->GetStartOffset(&startOffset); + PRUint32 count; + text->GetLength(&count); + if (startOffset==(PRInt32)count) + { + //if (gNoisy) { printf(" skipping node %p\n", content.get()); } + skipNode = PR_TRUE; + } + } + } + else + { // handle non-text leaf nodes here + PRBool canContainChildren; + content->CanContainChildren(canContainChildren); + if (PR_TRUE==canContainChildren) + { + //if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); } + skipNode = PR_TRUE; + } + else { + //if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); } + } + } + if (PR_FALSE==skipNode) + { + nsCOMPtrnode; + node = do_QueryInterface(content); + if (node) + { + PRBool isSet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode)); + if (PR_TRUE==first) + { + aFirst = isSet; + first = PR_FALSE; + } + if (PR_TRUE==isSet) { + aAny = PR_TRUE; + } + else { + aAll = PR_FALSE; + } + } + } + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + } + } + } + } + if (PR_FALSE==aAny) { // make sure that if none of the selection is set, we don't report all is set + aAll = PR_FALSE; + } + //if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); } + return result; } -NS_IMETHODIMP nsHTMLEditor::InsertBreak() +NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute) +{ + +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->RemoveTextProperty(aProperty, aAttribute); +#endif // ENABLE_JS_EDITOR_LOG + + if (!aProperty) { return NS_ERROR_NULL_POINTER; } + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("---------- start nsTextEditor::RemoveTextProperty %s ----------\n", propCString); } + delete [] propCString; + } + + nsresult result=NS_ERROR_NOT_INITIALIZED; + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + PRBool cancel; + nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty); + result = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) + { + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (PR_TRUE==isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, nsnull); + } + else + { + // removing text properties can really shuffle text nodes around + // so we need to keep some extra state to restore a reasonable selection + // after we're done + nsCOMPtr parentForSelection; // selection's block parent + PRInt32 rangeStartOffset, rangeEndOffset; + GetTextSelectionOffsetsForRange(selection, getter_AddRefs(parentForSelection), + rangeStartOffset, rangeEndOffset); + nsEditor::BeginTransaction(); + nsCOMPtr startParent, endParent; + PRInt32 startOffset, endOffset; + nsCOMPtr enumerator; + result = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtrcurrentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + nsCOMPtrcommonParent; + result = range->GetCommonParent(getter_AddRefs(commonParent)); + if ((NS_SUCCEEDED(result)) && commonParent) + { + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + range->GetStartParent(getter_AddRefs(startParent)); + range->GetEndParent(getter_AddRefs(endParent)); + if (startParent.get()==endParent.get()) + { // the range is entirely contained within a single text node + // commonParent==aStartParent, so get the "real" parent of the selection + startParent->GetParentNode(getter_AddRefs(commonParent)); + result = RemoveTextPropertiesForNode(startParent, commonParent, + startOffset, endOffset, + aProperty, nsnull); + } + else + { + result = RemoveTextPropertiesForNodeWithDifferentParents(startParent,startOffset, + endParent, endOffset, + commonParent, + aProperty, nsnull); + } + if (NS_SUCCEEDED(result)) + { // compute a range for the selection + // don't want to actually do anything with selection, because + // we are still iterating through it. Just want to create and remember + // an nsIDOMRange, and later add the range to the selection after clearing it. + // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism + // for setting a compound selection yet. + } + } + } + } + nsEditor::EndTransaction(); + if (NS_SUCCEEDED(result)) + { + ResetTextSelectionForRange(parentForSelection, rangeStartOffset, rangeEndOffset, selection); + } + } + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + } + if (gNoisy) + { + nsAutoString propString; + aProperty->ToString(propString); + char *propCString = propString.ToNewCString(); + if (gNoisy) { printf("---------- end nsTextEditor::RemoveTextProperty %s ----------\n", propCString); } + delete [] propCString; + } + return result; +} + +NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ESelectionCollapseDirection aAction) { #ifdef ENABLE_JS_EDITOR_LOG nsAutoJSEditorLogLock logLock(mJSEditorLog); if (mJSEditorLog) - mJSEditorLog->InsertBreak(); + mJSEditorLog->DeleteSelection(aAction); #endif // ENABLE_JS_EDITOR_LOG - nsresult res; if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } nsCOMPtr selection; PRBool cancel= PR_FALSE; - nsAutoEditBatch beginBatching(this); + nsresult result = nsEditor::BeginTransaction(); + if (NS_FAILED(result)) { return result; } // pre-process nsEditor::GetSelection(getter_AddRefs(selection)); - nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertBreak); - res = mRules->WillDoAction(selection, &ruleInfo, &cancel); - if ((PR_FALSE==cancel) && (NS_SUCCEEDED(res))) + nsTextRulesInfo ruleInfo(nsTextEditRules::kDeleteSelection); + ruleInfo.collapsedAction = aAction; + result = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) { - // create the new BR node - nsCOMPtr newNode; - nsAutoString tag("BR"); - res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (NS_SUCCEEDED(res) && newNode) - { - // set the selection to the new node - nsCOMPtrparent; - res = newNode->GetParentNode(getter_AddRefs(parent)); - if (NS_SUCCEEDED(res) && parent) - { - PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not - nsCOMPtrnextNode; - newNode->GetNextSibling(getter_AddRefs(nextNode)); - if (nextNode) - { - nsCOMPtrnextTextNode; - nextTextNode = do_QueryInterface(nextNode); - if (!nextTextNode) { - nextNode = do_QueryInterface(newNode); - } - else { - offsetInParent=0; - } - } - else { - nextNode = do_QueryInterface(newNode); - } - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(res)) - { - if (-1==offsetInParent) - { - nextNode->GetParentNode(getter_AddRefs(parent)); - res = GetChildOffset(nextNode, parent, offsetInParent); - if (NS_SUCCEEDED(res)) { - selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break - } - } - else - { - selection->Collapse(nextNode, offsetInParent); - } - } - } - } - // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE - res = mRules->DidDoAction(selection, &ruleInfo, res); + result = DeleteSelectionImpl(aAction); + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); } - return res; + nsresult endTxnResult = nsEditor::EndTransaction(); // don't return this result! + NS_ASSERTION ((NS_SUCCEEDED(endTxnResult)), "bad end transaction result"); + + return result; } -NS_IMETHODIMP nsHTMLEditor::GetParagraphFormat(nsString& aParagraphFormat) -{ - nsresult res = NS_ERROR_NOT_INITIALIZED; - - return res; -} - -NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat) +NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) { #ifdef ENABLE_JS_EDITOR_LOG nsAutoJSEditorLogLock logLock(mJSEditorLog); if (mJSEditorLog) - mJSEditorLog->SetParagraphFormat(aParagraphFormat); + mJSEditorLog->InsertText(aStringToInsert); #endif // ENABLE_JS_EDITOR_LOG - nsresult res = NS_ERROR_NOT_INITIALIZED; - //Kinda sad to waste memory just to force lower case - nsAutoString tag = aParagraphFormat; - tag.ToLowerCase(); - if (tag == "normal" || tag == "p") { - res = RemoveParagraphStyle(); - } else if (tag == "li") { - res = InsertList("ul"); - } else { - res = ReplaceBlockParent(tag); - } - return res; -} + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - -// Methods shared with the base editor. -// Note: We could call each of these via nsTextEditor -- is that better? -NS_IMETHODIMP nsHTMLEditor::EnableUndo(PRBool aEnable) -{ - return nsTextEditor::EnableUndo(aEnable); -} - -NS_IMETHODIMP nsHTMLEditor::Undo(PRUint32 aCount) -{ - return nsTextEditor::Undo(aCount); -} - -NS_IMETHODIMP nsHTMLEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo) -{ - return nsTextEditor::CanUndo(aIsEnabled, aCanUndo); -} - -NS_IMETHODIMP nsHTMLEditor::Redo(PRUint32 aCount) -{ - return nsTextEditor::Redo(aCount); -} - -NS_IMETHODIMP nsHTMLEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo) -{ - return nsTextEditor::CanRedo(aIsEnabled, aCanRedo); -} - -NS_IMETHODIMP nsHTMLEditor::BeginTransaction() -{ - return nsTextEditor::BeginTransaction(); -} - -NS_IMETHODIMP nsHTMLEditor::EndTransaction() -{ - return nsTextEditor::EndTransaction(); -} - -NS_IMETHODIMP nsHTMLEditor::MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::MoveSelectionUp(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::MoveSelectionDown(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::MoveSelectionNext(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::MoveSelectionPrevious(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::SelectNext(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) -{ - return nsTextEditor::SelectPrevious(aIncrement, aExtendSelection); -} - -NS_IMETHODIMP nsHTMLEditor::SelectAll() -{ - return nsTextEditor::SelectAll(); -} - -NS_IMETHODIMP nsHTMLEditor::BeginningOfDocument() -{ - return nsEditor::BeginningOfDocument(); -} - -NS_IMETHODIMP nsHTMLEditor::EndOfDocument() -{ - return nsEditor::EndOfDocument(); -} - -NS_IMETHODIMP nsHTMLEditor::ScrollUp(nsIAtom *aIncrement) -{ - return nsTextEditor::ScrollUp(aIncrement); -} - -NS_IMETHODIMP nsHTMLEditor::ScrollDown(nsIAtom *aIncrement) -{ - return nsTextEditor::ScrollDown(aIncrement); -} - -NS_IMETHODIMP nsHTMLEditor::ScrollIntoView(PRBool aScrollToBegin) -{ - return nsTextEditor::ScrollIntoView(aScrollToBegin); -} - -NS_IMETHODIMP nsHTMLEditor::Save() -{ - return nsTextEditor::Save(); -} - -NS_IMETHODIMP nsHTMLEditor::SaveAs(PRBool aSavingCopy) -{ - return nsTextEditor::SaveAs(aSavingCopy); -} - -NS_IMETHODIMP nsHTMLEditor::Cut() -{ - return nsTextEditor::Cut(); -} - -NS_IMETHODIMP nsHTMLEditor::Copy() -{ - return nsTextEditor::Copy(); -} - -NS_IMETHODIMP nsHTMLEditor::Paste() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->Paste(); -#endif // ENABLE_JS_EDITOR_LOG - - nsString stuffToPaste; - - // Get Clipboard Service - nsIClipboard* clipboard; - nsresult rv = nsServiceManager::GetService(kCClipboardCID, - nsIClipboard::GetIID(), - (nsISupports **)&clipboard); - - // Create generic Transferable for getting the data - nsCOMPtr trans; - rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, - nsITransferable::GetIID(), - (void**) getter_AddRefs(trans)); - if (NS_SUCCEEDED(rv)) - { - // Get the nsITransferable interface for getting the data from the clipboard - if (trans) - { - // Create the desired DataFlavor for the type of data - // we want to get out of the transferable - nsAutoString htmlFlavor(kHTMLMime); - nsAutoString textFlavor(kTextMime); - nsAutoString imageFlavor(kJPEGImageMime); - - trans->AddDataFlavor(&htmlFlavor); - trans->AddDataFlavor(&textFlavor); - trans->AddDataFlavor(&imageFlavor); - - // Get the Data from the clipboard - if (NS_SUCCEEDED(clipboard->GetData(trans))) - { - nsAutoString flavor; - char * data; - PRUint32 len; - if (NS_SUCCEEDED(trans->GetAnyTransferData(&flavor, (void **)&data, &len))) - { -#ifdef DEBUG_akkana - printf("Got flavor [%s]\n", flavor.ToNewCString()); -#endif - if (flavor.Equals(htmlFlavor)) - { - if (data && len > 0) // stuffToPaste is ready for insertion into the content - { - stuffToPaste.SetString(data, len); - rv = InsertHTML(stuffToPaste); - } - } - else if (flavor.Equals(textFlavor)) - { - if (data && len > 0) // stuffToPaste is ready for insertion into the content - { - stuffToPaste.SetString(data, len); - rv = InsertText(stuffToPaste); - } - } - else if (flavor.Equals(imageFlavor)) - { - // Insert Image code here - printf("Don't know how to insert an image yet!\n"); - //nsIImage* image = (nsIImage *)data; - //NS_RELEASE(image); - rv = NS_ERROR_FAILURE; // for now give error code - } - } - - } - } - } - nsServiceManager::ReleaseService(kCClipboardCID, clipboard); - - return rv; -} - -// -// HTML PasteAsQuotation: Paste in a blockquote type=cite -// -NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation() -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->PasteAsQuotation(); -#endif // ENABLE_JS_EDITOR_LOG - - nsAutoString citation(""); - return PasteAsCitedQuotation(citation); -} - -NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->PasteAsCitedQuotation(aCitation); -#endif // ENABLE_JS_EDITOR_LOG - - nsAutoEditBatch beginBatching(this); - nsCOMPtr newNode; - nsAutoString tag("blockquote"); - nsresult res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (NS_FAILED(res) || !newNode) - return res; - - // Try to set type=cite. Ignore it if this fails. - nsCOMPtr newElement (do_QueryInterface(newNode)); - if (newElement) - { - nsAutoString type ("type"); - nsAutoString cite ("cite"); - newElement->SetAttribute(type, cite); - } - - // Set the selection to the underneath the node we just inserted: nsCOMPtr selection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res) || !selection) - { -#ifdef DEBUG_akkana - printf("Can't get selection!"); -#endif - } + PRBool cancel= PR_FALSE; - res = selection->Collapse(newNode, 0); - if (NS_FAILED(res)) - { -#ifdef DEBUG_akkana - printf("Couldn't collapse"); -#endif - } + // pre-process + nsEditor::GetSelection(getter_AddRefs(selection)); + nsString resultString; + PlaceholderTxn *placeholderTxn=nsnull; + nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertText); + ruleInfo.placeTxn = &placeholderTxn; + ruleInfo.inString = &aStringToInsert; + ruleInfo.outString = &resultString; + ruleInfo.typeInState = *mTypeInState; + ruleInfo.maxLength = mMaxTextLength; - res = Paste(); - return res; + nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) + { + result = InsertTextImpl(resultString); + // post-process + result = mRules->DidDoAction(selection, &ruleInfo, result); + } + if (placeholderTxn) + placeholderTxn->SetAbsorb(PR_FALSE); // this ends the merging of txns into placeholderTxn + + return result; } -NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertAsQuotation(aQuotedText); -#endif // ENABLE_JS_EDITOR_LOG - - nsAutoString citation (""); - return InsertAsCitedQuotation(aQuotedText, citation); -} - -NS_IMETHODIMP nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText, - const nsString& aCitation) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertAsCitedQuotation(aQuotedText, aCitation); -#endif // ENABLE_JS_EDITOR_LOG - - nsAutoEditBatch beginBatching(this); - nsCOMPtr newNode; - nsAutoString tag("blockquote"); - nsresult res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (NS_FAILED(res) || !newNode) - return res; - - // Try to set type=cite. Ignore it if this fails. - nsCOMPtr newElement (do_QueryInterface(newNode)); - if (newElement) - { - nsAutoString type ("type"); - nsAutoString cite ("cite"); - newElement->SetAttribute(type, cite); - - if (aCitation.Length() > 0) - newElement->SetAttribute(cite, aCitation); - } - - res = InsertHTML(aQuotedText); - return res; -} NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) { @@ -798,39 +962,367 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) } -NS_IMETHODIMP nsHTMLEditor::OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags) +NS_IMETHODIMP nsHTMLEditor::InsertBreak() { - return nsTextEditor::OutputToString(aOutputString, aFormatType, aFlags); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->InsertBreak(); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res; + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + PRBool cancel= PR_FALSE; + + nsAutoEditBatch beginBatching(this); + + // pre-process + nsEditor::GetSelection(getter_AddRefs(selection)); + nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertBreak); + res = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if ((PR_FALSE==cancel) && (NS_SUCCEEDED(res))) + { + // create the new BR node + nsCOMPtr newNode; + nsAutoString tag("BR"); + res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (NS_SUCCEEDED(res) && newNode) + { + // set the selection to the new node + nsCOMPtrparent; + res = newNode->GetParentNode(getter_AddRefs(parent)); + if (NS_SUCCEEDED(res) && parent) + { + PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not + nsCOMPtrnextNode; + newNode->GetNextSibling(getter_AddRefs(nextNode)); + if (nextNode) + { + nsCOMPtrnextTextNode; + nextTextNode = do_QueryInterface(nextNode); + if (!nextTextNode) { + nextNode = do_QueryInterface(newNode); + } + else { + offsetInParent=0; + } + } + else { + nextNode = do_QueryInterface(newNode); + } + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(res)) + { + if (-1==offsetInParent) + { + nextNode->GetParentNode(getter_AddRefs(parent)); + res = GetChildOffset(nextNode, parent, offsetInParent); + if (NS_SUCCEEDED(res)) { + selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break + } + } + else + { + selection->Collapse(nextNode, offsetInParent); + } + } + } + } + // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE + res = mRules->DidDoAction(selection, &ruleInfo, res); + } + + return res; } -NS_IMETHODIMP nsHTMLEditor::OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharset, - PRUint32 aFlags) + +NS_IMETHODIMP +nsHTMLEditor::InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection) { - return nsTextEditor::OutputToStream(aOutputStream, aFormatType, - aCharset, aFlags); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->InsertElement(aElement, aDeleteSelection); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res = NS_ERROR_NOT_INITIALIZED; + + if (!aElement) + return NS_ERROR_NULL_POINTER; + + nsAutoEditBatch beginBatching(this); + // For most elements, set caret after inserting + //PRBool setCaretAfterElement = PR_TRUE; + + nsCOMPtrselection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (!NS_SUCCEEDED(res) || !selection) + return NS_ERROR_FAILURE; + + // Clear current selection. + // Should put caret at anchor point? + if (!aDeleteSelection) + { + PRBool collapseAfter = PR_TRUE; + // Named Anchor is a special case, + // We collapse to insert element BEFORE the selection + // For all other tags, we insert AFTER the selection + nsCOMPtr node = do_QueryInterface(aElement); + if (IsNamedAnchorNode(node)) + collapseAfter = PR_FALSE; + + if (collapseAfter) + { + // Default behavior is to collapse to the end of the selection + selection->ClearSelection(); + } else { + // Collapse to the start of the selection, + // We must explore the first range and find + // its parent and starting offset of selection + // TODO: Move this logic to a new method nsIDOMSelection::CollapseToStart()??? + nsCOMPtr firstRange; + res = selection->GetRangeAt(0, getter_AddRefs(firstRange)); + if (NS_SUCCEEDED(res) && firstRange) + { + nsCOMPtr parent; + res = firstRange->GetCommonParent(getter_AddRefs(parent)); + if (NS_SUCCEEDED(res) && parent) + { + PRInt32 startOffset; + firstRange->GetStartOffset(&startOffset); + selection->Collapse(parent, startOffset); + } else { + // Very unlikely, but collapse to the end if we failed above + selection->ClearSelection(); + } + } + } + } + + nsCOMPtr parentSelectedNode; + PRInt32 offsetForInsert; + res = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); + if (NS_SUCCEEDED(res) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetForInsert)) && parentSelectedNode) + { +#ifdef DEBUG_cmanske + { + nsAutoString name; + parentSelectedNode->GetNodeName(name); + printf("InsertElement: Anchor node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n", offsetForInsert); + } +#endif + nsAutoString tagName; + aElement->GetNodeName(tagName); + tagName.ToLowerCase(); + nsCOMPtr parent = parentSelectedNode; + nsCOMPtr topChild = parentSelectedNode; + nsCOMPtr tmp; + nsAutoString parentTagName; + PRBool isRoot; + + // Search up the parent chain to find a suitable container + while (!CanContainTag(parent, tagName)) + { + // If the current parent is a root (body or table cell) + // then go no further - we can't insert + parent->GetNodeName(parentTagName); + res = IsRootTag(parentTagName, isRoot); + if (!NS_SUCCEEDED(res) || isRoot) + return NS_ERROR_FAILURE; + // Get the next parent + parent->GetParentNode(getter_AddRefs(tmp)); + if (!tmp) + return NS_ERROR_FAILURE; + topChild = parent; + parent = tmp; + } +#ifdef DEBUG_cmanske + { + nsAutoString name; + parent->GetNodeName(name); + printf("Parent node to insert under: "); + wprintf(name.GetUnicode()); + printf("\n"); + topChild->GetNodeName(name); + printf("TopChild to split: "); + wprintf(name.GetUnicode()); + printf("\n"); + } +#endif + if (parent != topChild) + { + // we need to split some levels above the original selection parent + res = SplitNodeDeep(topChild, parentSelectedNode, offsetForInsert); + if (NS_FAILED(res)) + return res; + // topChild went to the right on the split + // so this is the offset to insert at + offsetForInsert = GetIndexOf(parent,topChild); + } + // Now we can insert the new node + res = InsertNode(aElement, parent, offsetForInsert); + + // Set caret after element, but check for special case + // of inserting table-related elements: set in first cell instead + if (!SetCaretInTableCell(aElement)) + res = SetCaretAfterElement(aElement); + + } + return res; +} + + +NS_IMETHODIMP +nsHTMLEditor::DeleteSelectionAndCreateNode(const nsString& aTag, + nsIDOMNode ** aNewNode) +{ + nsCOMPtr parentSelectedNode; + PRInt32 offsetOfNewNode; + nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, + offsetOfNewNode); + if (!NS_SUCCEEDED(result)) + return result; + + nsCOMPtr newNode; + result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode, + getter_AddRefs(newNode)); + + *aNewNode = newNode; + + // we want the selection to be just after the new node + nsCOMPtr selection; + result = GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + selection->Collapse(parentSelectedNode, offsetOfNewNode+1); + + return result; } NS_IMETHODIMP -nsHTMLEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) +nsHTMLEditor::SelectElement(nsIDOMElement* aElement) { - return nsTextEditor::CopyAttributes(aDestNode, aSourceNode); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SelectElement(aElement); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res = NS_ERROR_NULL_POINTER; + + // Must be sure that element is contained in the document body + if (IsElementInBody(aElement)) + { + nsCOMPtr selection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(res) && selection) + { + nsCOMPtrparent; + res = aElement->GetParentNode(getter_AddRefs(parent)); + if (NS_SUCCEEDED(res) && parent) + { + PRInt32 offsetInParent; + res = GetChildOffset(aElement, parent, offsetInParent); + + if (NS_SUCCEEDED(res)) + { + // Collapse selection to just before desired element, + selection->Collapse(parent, offsetInParent); + // then extend it to just after + selection->Extend(parent, offsetInParent+1); + } + } + } + } + return res; } NS_IMETHODIMP -nsHTMLEditor::ApplyStyleSheet(const nsString& aURL) +nsHTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement) { - return nsTextEditor::ApplyStyleSheet(aURL); +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SetCaretAfterElement(aElement); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res = NS_ERROR_NULL_POINTER; + + // Be sure the element is contained in the document body + if (aElement && IsElementInBody(aElement)) + { + nsCOMPtr selection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(res) && selection) + { + nsCOMPtrparent; + res = aElement->GetParentNode(getter_AddRefs(parent)); + if (NS_SUCCEEDED(res) && parent) + { + PRInt32 offsetInParent; + res = GetChildOffset(aElement, parent, offsetInParent); + if (NS_SUCCEEDED(res)) + { + // Collapse selection to just after desired element, + selection->Collapse(parent, offsetInParent+1); +#ifdef DEBUG_cmanske + { + nsAutoString name; + parent->GetNodeName(name); + printf("SetCaretAfterElement: Parent node: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n\nHTML:\n", offsetInParent+1); + nsString Format("text/html"); + nsString ContentsAs; + OutputToString(ContentsAs, Format, 2); + wprintf(ContentsAs.GetUnicode()); + } +#endif + } + } + } + } + return res; +} + + +NS_IMETHODIMP nsHTMLEditor::GetParagraphFormat(nsString& aParagraphFormat) +{ + nsresult res = NS_ERROR_NOT_INITIALIZED; + + return res; +} + +NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SetParagraphFormat(aParagraphFormat); +#endif // ENABLE_JS_EDITOR_LOG + + nsresult res = NS_ERROR_NOT_INITIALIZED; + //Kinda sad to waste memory just to force lower case + nsAutoString tag = aParagraphFormat; + tag.ToLowerCase(); + if (tag == "normal" || tag == "p") { + res = RemoveParagraphStyle(); + } else if (tag == "li") { + res = InsertList("ul"); + } else { + res = ReplaceBlockParent(tag); + } + return res; } -//================================================================ -// HTML Editor methods -// -// Note: Table Editing methods are implemented in EditTable.cpp -// // get the paragraph style(s) for the selection NS_IMETHODIMP @@ -996,354 +1488,6 @@ nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) return res; } -NS_IMETHODIMP -nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, - nsString &aParentTag, - BlockTransformationType aTransformation) -{ - if (!aNode) { return NS_ERROR_NULL_POINTER; } - if (gNoisy) - { - char *tag = aParentTag.ToNewCString(); - printf("---------- ReParentContentOfNode(%p,%s,%d) -----------\n", aNode, tag, aTransformation); - delete [] tag; - } - // find the current block parent, or just use aNode if it is a block node - nsCOMPtrblockParentElement; - nsCOMPtrnodeToReParent; // this is the node we'll operate on, by default it's aNode - nsresult res = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(nodeToReParent)); - PRBool nodeIsInline; - PRBool nodeIsBlock=PR_FALSE; - nsTextEditor::IsNodeInline(aNode, nodeIsInline); - if (PR_FALSE==nodeIsInline) - { - nsresult QIResult; - nsCOMPtrnodeAsText; - QIResult = aNode->QueryInterface(nsIDOMCharacterData::GetIID(), getter_AddRefs(nodeAsText)); - if (NS_FAILED(QIResult) || !nodeAsText) { - nodeIsBlock=PR_TRUE; - } - } - // if aNode is the block parent, then the node to reparent is one of its children - if (PR_TRUE==nodeIsBlock) - { - res = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(blockParentElement)); - if (NS_SUCCEEDED(res) && blockParentElement) { - res = aNode->GetFirstChild(getter_AddRefs(nodeToReParent)); - } - } - else { // we just need to get the block parent of aNode - res = nsTextEditor::GetBlockParent(aNode, getter_AddRefs(blockParentElement)); - } - // at this point, we must have a good res, a node to reparent, and a block parent - if (!nodeToReParent) { return NS_ERROR_UNEXPECTED;} - if (!blockParentElement) { return NS_ERROR_NULL_POINTER;} - if (NS_SUCCEEDED(res)) - { - nsCOMPtr newParentNode; - nsCOMPtr blockParentNode = do_QueryInterface(blockParentElement); - // we need to treat nodes directly inside the body differently - nsAutoString parentTag; - blockParentElement->GetTagName(parentTag); - PRBool isRoot; - IsRootTag(parentTag, isRoot); - if (PR_TRUE==isRoot) - { - // if nodeToReParent is a text node, we have Text. - // re-parent Text into a new at the offset of Text in - // so we end up with Text - // ignore aTransformation, replaces act like inserts - nsCOMPtr nodeAsText = do_QueryInterface(nodeToReParent); - if (nodeAsText) - { - res = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, - aTransformation, getter_AddRefs(newParentNode)); - } - else - { // this is the case of an insertion point between 2 non-text objects - // XXX: how to you know it's an insertion point??? - PRInt32 offsetInParent=0; - res = GetChildOffset(nodeToReParent, blockParentNode, offsetInParent); - NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); - // otherwise, just create the block parent at the selection - res = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, - getter_AddRefs(newParentNode)); - // XXX: need to move some of the children of blockParentNode into the newParentNode? - // XXX: need to create a bogus text node inside this new block? - // that means, I need to generalize bogus node handling - } - } - else - { // the block parent is not a ROOT, - // for the selected block content, transform blockParentNode - if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) || - (eInsertParent==aTransformation)) - { - if (gNoisy) { DebugDumpContent(); } // DEBUG - res = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, - aTransformation, getter_AddRefs(newParentNode)); - if ((NS_SUCCEEDED(res)) && (newParentNode) && (eReplaceParent==aTransformation)) - { - PRBool hasChildren; - blockParentNode->HasChildNodes(&hasChildren); - if (PR_FALSE==hasChildren) - { - res = nsEditor::DeleteNode(blockParentNode); - if (gNoisy) - { - printf("deleted old block parent node %p\n", blockParentNode.get()); - DebugDumpContent(); // DEBUG - } - } - } - } - else { // otherwise, it's a no-op - if (gNoisy) { printf("AddBlockParent is a no-op for this collapsed selection.\n"); } - } - } - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, - nsString &aParentTag, - nsIDOMNode *aBlockParentNode, - nsString &aBlockParentTag, - BlockTransformationType aTransformation, - nsIDOMNode **aNewParentNode) -{ - if (!aNode || !aBlockParentNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; } - nsCOMPtr blockParentNode = do_QueryInterface(aBlockParentNode); - PRBool removeBlockParent = PR_FALSE; - PRBool removeBreakBefore = PR_FALSE; - PRBool removeBreakAfter = PR_FALSE; - nsCOMPtrancestor; - nsresult res = aNode->GetParentNode(getter_AddRefs(ancestor)); - nsCOMPtrpreviousAncestor = do_QueryInterface(aNode); - while (NS_SUCCEEDED(res) && ancestor) - { - nsCOMPtrancestorElement = do_QueryInterface(ancestor); - nsAutoString ancestorTag; - ancestorElement->GetTagName(ancestorTag); - if (ancestorTag.EqualsIgnoreCase(aBlockParentTag)) - { - break; // previousAncestor will contain the node to operate on - } - previousAncestor = do_QueryInterface(ancestor); - res = ancestorElement->GetParentNode(getter_AddRefs(ancestor)); - } - // now, previousAncestor is the node we are operating on - nsCOMPtrleftNode, rightNode; - res = GetBlockSection(previousAncestor, - getter_AddRefs(leftNode), - getter_AddRefs(rightNode)); - if ((NS_SUCCEEDED(res)) && leftNode && rightNode) - { - // determine some state for managing
s around the new block - PRBool isSubordinateBlock = PR_FALSE; // if true, the content is already in a subordinate block - PRBool isRootBlock = PR_FALSE; // if true, the content is in a root block - nsCOMPtrblockParentElement = do_QueryInterface(blockParentNode); - if (blockParentElement) - { - nsAutoString blockParentTag; - blockParentElement->GetTagName(blockParentTag); - IsSubordinateBlock(blockParentTag, isSubordinateBlock); - IsRootTag(blockParentTag, isRootBlock); - } - - if (PR_TRUE==isRootBlock) - { // we're creating a block element where a block element did not previously exist - removeBreakBefore = PR_TRUE; - removeBreakAfter = PR_TRUE; - } - - // apply the transformation - PRInt32 offsetInParent=0; - if (eInsertParent==aTransformation || PR_TRUE==isRootBlock) - { - res = GetChildOffset(leftNode, blockParentNode, offsetInParent); - NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); - res = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); - if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } - } - else - { - nsCOMPtr grandParent; - res = blockParentNode->GetParentNode(getter_AddRefs(grandParent)); - if ((NS_SUCCEEDED(res)) && grandParent) - { - nsCOMPtrfirstChildNode, lastChildNode; - blockParentNode->GetFirstChild(getter_AddRefs(firstChildNode)); - blockParentNode->GetLastChild(getter_AddRefs(lastChildNode)); - if (firstChildNode==leftNode && lastChildNode==rightNode) - { - res = GetChildOffset(blockParentNode, grandParent, offsetInParent); - NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); - res = nsTextEditor::CreateNode(aParentTag, grandParent, offsetInParent, aNewParentNode); - if (gNoisy) { printf("created a node in grandParent at offset %d\n", offsetInParent); } - } - else - { - // We're in the case where the content of blockParentNode is separated by
's, - // creating multiple block content ranges. - // Split blockParentNode around the blockContent - if (gNoisy) { printf("splitting a node because of
s\n"); } - nsCOMPtr newLeftNode; - if (firstChildNode!=leftNode) - { - res = GetChildOffset(leftNode, blockParentNode, offsetInParent); - if (gNoisy) { printf("splitting left at %d\n", offsetInParent); } - res = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); - // after this split, blockParentNode still contains leftNode and rightNode - } - if (lastChildNode!=rightNode) - { - res = GetChildOffset(rightNode, blockParentNode, offsetInParent); - offsetInParent++; - if (gNoisy) { printf("splitting right at %d\n", offsetInParent); } - res = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); - blockParentNode = do_QueryInterface(newLeftNode); - } - res = GetChildOffset(leftNode, blockParentNode, offsetInParent); - NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); - res = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); - if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } - // what we need to do here is remove the existing block parent when we're all done. - removeBlockParent = PR_TRUE; - } - } - } - if ((NS_SUCCEEDED(res)) && *aNewParentNode) - { // move all the children/contents of blockParentNode to aNewParentNode - nsCOMPtrchildNode = do_QueryInterface(rightNode); - nsCOMPtrpreviousSiblingNode; - while (NS_SUCCEEDED(res) && childNode) - { - childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); - // explicitly delete of childNode from it's current parent - // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! - res = nsEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) - { - res = nsEditor::InsertNode(childNode, *aNewParentNode, 0); - if (gNoisy) - { - printf("re-parented sibling node %p\n", childNode.get()); - } - } - if (childNode==leftNode || rightNode==leftNode) { - break; - } - childNode = do_QueryInterface(previousSiblingNode); - } // end while loop - } - // clean up the surrounding content to maintain vertical whitespace - if (NS_SUCCEEDED(res)) - { - // if the prior node is a
and we did something to change vertical whitespacing, delete the
- nsCOMPtr brNode; - res = GetPriorNode(leftNode, PR_TRUE, getter_AddRefs(brNode)); - if (NS_SUCCEEDED(res) && brNode) - { - nsCOMPtr brContent = do_QueryInterface(brNode); - if (brContent) - { - nsCOMPtr brContentTag; - brContent->GetTag(*getter_AddRefs(brContentTag)); - if (nsIEditProperty::br==brContentTag.get()) { - res = DeleteNode(brNode); - } - } - } - - // if the next node is a
and we did something to change vertical whitespacing, delete the
- if (NS_SUCCEEDED(res)) - { - res = GetNextNode(rightNode, PR_TRUE, getter_AddRefs(brNode)); - if (NS_SUCCEEDED(res) && brNode) - { - nsCOMPtr brContent = do_QueryInterface(brNode); - if (brContent) - { - nsCOMPtr brContentTag; - brContent->GetTag(*getter_AddRefs(brContentTag)); - if (nsIEditProperty::br==brContentTag.get()) { - res = DeleteNode(brNode); - } - } - } - } - } - if ((NS_SUCCEEDED(res)) && (PR_TRUE==removeBlockParent)) - { // we determined we need to remove the previous block parent. Do it! - // go through list backwards so deletes don't interfere with the iteration - nsCOMPtr childNodes; - res = blockParentNode->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(res)) && (childNodes)) - { - nsCOMPtrgrandParent; - blockParentNode->GetParentNode(getter_AddRefs(grandParent)); - //PRInt32 offsetInParent; - res = GetChildOffset(blockParentNode, grandParent, offsetInParent); - PRUint32 childCount; - childNodes->GetLength(&childCount); - PRInt32 i=childCount-1; - for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) - { - nsCOMPtr childNode; - res = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(res)) && (childNode)) - { - res = nsTextEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); - } - } - } - if (gNoisy) { printf("removing the old block parent %p\n", blockParentNode.get()); } - res = nsTextEditor::DeleteNode(blockParentNode); - } - } - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, - nsString &aParentTag, - BlockTransformationType aTranformation) -{ - if (!aRange) { return NS_ERROR_NULL_POINTER; } - nsresult res; - nsISupportsArray *blockSections; - res = NS_NewISupportsArray(&blockSections); - if ((NS_SUCCEEDED(res)) && blockSections) - { - res = GetBlockSectionsForRange(aRange, blockSections); - if (NS_SUCCEEDED(res)) - { - nsIDOMRange *subRange; - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - while (subRange && (NS_SUCCEEDED(res))) - { - nsCOMPtrstartParent; - res = subRange->GetStartParent(getter_AddRefs(startParent)); - if (NS_SUCCEEDED(res) && startParent) - { - if (gNoisy) { printf("ReParentContentOfRange calling ReParentContentOfNode\n"); } - res = ReParentContentOfNode(startParent, aParentTag, aTranformation); - } - NS_RELEASE(subRange); - blockSections->RemoveElementAt(0); - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - } - } - NS_RELEASE(blockSections); - } - return res; -} NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyle() @@ -1383,88 +1527,6 @@ nsHTMLEditor::RemoveParagraphStyle() return res; } -NS_IMETHODIMP -nsHTMLEditor::RemoveParagraphStyleFromRange(nsIDOMRange *aRange) -{ - if (!aRange) { return NS_ERROR_NULL_POINTER; } - nsresult res; - nsISupportsArray *blockSections; - res = NS_NewISupportsArray(&blockSections); - if ((NS_SUCCEEDED(res)) && blockSections) - { - res = GetBlockSectionsForRange(aRange, blockSections); - if (NS_SUCCEEDED(res)) - { - nsIDOMRange *subRange; - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - while (subRange && (NS_SUCCEEDED(res))) - { - res = RemoveParagraphStyleFromBlockContent(subRange); - NS_RELEASE(subRange); - blockSections->RemoveElementAt(0); - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - } - } - NS_RELEASE(blockSections); - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) -{ - if (!aRange) { return NS_ERROR_NULL_POINTER; } - nsresult res; - nsCOMPtrstartParent; - aRange->GetStartParent(getter_AddRefs(startParent)); - nsCOMPtrblockParentElement; - res = nsTextEditor::GetBlockParent(startParent, getter_AddRefs(blockParentElement)); - while ((NS_SUCCEEDED(res)) && blockParentElement) - { - nsAutoString blockParentTag; - blockParentElement->GetTagName(blockParentTag); - PRBool isSubordinateBlock; - IsSubordinateBlock(blockParentTag, isSubordinateBlock); - if (PR_FALSE==isSubordinateBlock) { - break; - } - else - { - // go through list backwards so deletes don't interfere with the iteration - nsCOMPtr childNodes; - res = blockParentElement->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(res)) && (childNodes)) - { - nsCOMPtrgrandParent; - blockParentElement->GetParentNode(getter_AddRefs(grandParent)); - PRInt32 offsetInParent; - res = GetChildOffset(blockParentElement, grandParent, offsetInParent); - PRUint32 childCount; - childNodes->GetLength(&childCount); - PRInt32 i=childCount-1; - for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) - { - nsCOMPtr childNode; - res = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(res)) && (childNode)) - { - res = nsTextEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); - } - } - } - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::DeleteNode(blockParentElement); - } - } - } - res = nsTextEditor::GetBlockParent(startParent, getter_AddRefs(blockParentElement)); - } - return res; -} - - NS_IMETHODIMP nsHTMLEditor::RemoveParent(const nsString &aParentTag) { @@ -1503,94 +1565,98 @@ nsHTMLEditor::RemoveParent(const nsString &aParentTag) return res; } -NS_IMETHODIMP -nsHTMLEditor::RemoveParentFromRange(const nsString &aParentTag, nsIDOMRange *aRange) +NS_IMETHODIMP +nsHTMLEditor::InsertList(const nsString& aListType) { - if (!aRange) { return NS_ERROR_NULL_POINTER; } +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->InsertList(aListType); +#endif // ENABLE_JS_EDITOR_LOG + nsresult res; - nsISupportsArray *blockSections; - res = NS_NewISupportsArray(&blockSections); - if ((NS_SUCCEEDED(res)) && blockSections) + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + + nsCOMPtr selection; + PRBool cancel= PR_FALSE; + + nsAutoEditBatch beginBatching(this); + + // pre-process + nsEditor::GetSelection(getter_AddRefs(selection)); + nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeList); + if (aListType == "ol") ruleInfo.bOrdered = PR_TRUE; + else ruleInfo.bOrdered = PR_FALSE; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel); + if (cancel || (NS_FAILED(res))) return res; + + // Find out if the selection is collapsed: + if (NS_FAILED(res) || !selection) return res; + + PRBool isCollapsed; + res = selection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + + nsCOMPtr node; + PRInt32 offset; + + res = GetStartNodeAndOffset(selection, &node, &offset); + if (!node) res = NS_ERROR_FAILURE; + if (NS_FAILED(res)) return res; + + if (isCollapsed) { - res = GetBlockSectionsForRange(aRange, blockSections); - if (NS_SUCCEEDED(res)) + // have to find a place to put the list + nsCOMPtr parent = node; + nsCOMPtr topChild = node; + nsCOMPtr tmp; + + while ( !CanContainTag(parent, aListType)) { - nsIDOMRange *subRange; - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - while (subRange && (NS_SUCCEEDED(res))) - { - res = RemoveParentFromBlockContent(aParentTag, subRange); - NS_RELEASE(subRange); - blockSections->RemoveElementAt(0); - subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); - } + parent->GetParentNode(getter_AddRefs(tmp)); + if (!tmp) return NS_ERROR_FAILURE; + topChild = parent; + parent = tmp; } - NS_RELEASE(blockSections); + + if (parent != node) + { + // we need to split up to the child of parent + res = SplitNodeDeep(topChild, node, offset); + if (NS_FAILED(res)) return res; + // topChild already went to the right on the split + // so we don't need to add one to offset when figuring + // out where to plop list + offset = GetIndexOf(parent,topChild); + } + + // make a list + nsCOMPtr newList; + res = CreateNode(aListType, parent, offset, getter_AddRefs(newList)); + if (NS_FAILED(res)) return res; + // make a list item + nsAutoString tag("li"); + nsCOMPtr newItem; + res = CreateNode(tag, newList, 0, getter_AddRefs(newItem)); + if (NS_FAILED(res)) return res; + // put a space in it so layout will draw the list item + // XXX - revisit when layout is fixed + res = selection->Collapse(newItem,0); + if (NS_FAILED(res)) return res; + nsAutoString theText(" "); + res = InsertText(theText); + if (NS_FAILED(res)) return res; + // reposition selection to before the space character + res = GetStartNodeAndOffset(selection, &node, &offset); + if (NS_FAILED(res)) return res; + res = selection->Collapse(node,0); + if (NS_FAILED(res)) return res; } + return res; } -NS_IMETHODIMP -nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRange *aRange) -{ - if (!aRange) { return NS_ERROR_NULL_POINTER; } - nsresult res; - nsCOMPtrstartParent; - res = aRange->GetStartParent(getter_AddRefs(startParent)); - if ((NS_SUCCEEDED(res)) && startParent) - { - nsCOMPtrparentNode; - nsCOMPtrparentElement; - res = startParent->GetParentNode(getter_AddRefs(parentNode)); - while ((NS_SUCCEEDED(res)) && parentNode) - { - parentElement = do_QueryInterface(parentNode); - nsAutoString parentTag; - parentElement->GetTagName(parentTag); - PRBool isRoot; - IsRootTag(parentTag, isRoot); - if (aParentTag.EqualsIgnoreCase(parentTag)) - { - // go through list backwards so deletes don't interfere with the iteration - nsCOMPtr childNodes; - res = parentElement->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(res)) && (childNodes)) - { - nsCOMPtrgrandParent; - parentElement->GetParentNode(getter_AddRefs(grandParent)); - PRInt32 offsetInParent; - res = GetChildOffset(parentElement, grandParent, offsetInParent); - PRUint32 childCount; - childNodes->GetLength(&childCount); - PRInt32 i=childCount-1; - for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) - { - nsCOMPtr childNode; - res = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(res)) && (childNode)) - { - res = nsTextEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); - } - } - } - if (NS_SUCCEEDED(res)) { - res = nsTextEditor::DeleteNode(parentElement); - } - } - break; - } - else if (PR_TRUE==isRoot) { // hit a local root node, terminate loop - break; - } - res = parentElement->GetParentNode(getter_AddRefs(parentNode)); - } - } - return res; -} - - // TODO: Implement "outdent" NS_IMETHODIMP nsHTMLEditor::Indent(const nsString& aIndent) @@ -1708,98 +1774,6 @@ nsHTMLEditor::Align(const nsString& aAlignType) } -NS_IMETHODIMP -nsHTMLEditor::InsertList(const nsString& aListType) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertList(aListType); -#endif // ENABLE_JS_EDITOR_LOG - - nsresult res; - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } - - nsCOMPtr selection; - PRBool cancel= PR_FALSE; - - nsAutoEditBatch beginBatching(this); - - // pre-process - nsEditor::GetSelection(getter_AddRefs(selection)); - nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeList); - if (aListType == "ol") ruleInfo.bOrdered = PR_TRUE; - else ruleInfo.bOrdered = PR_FALSE; - res = mRules->WillDoAction(selection, &ruleInfo, &cancel); - if (cancel || (NS_FAILED(res))) return res; - - // Find out if the selection is collapsed: - if (NS_FAILED(res) || !selection) return res; - - PRBool isCollapsed; - res = selection->GetIsCollapsed(&isCollapsed); - if (NS_FAILED(res)) return res; - - nsCOMPtr node; - PRInt32 offset; - - res = GetStartNodeAndOffset(selection, &node, &offset); - if (!node) res = NS_ERROR_FAILURE; - if (NS_FAILED(res)) return res; - - if (isCollapsed) - { - // have to find a place to put the list - nsCOMPtr parent = node; - nsCOMPtr topChild = node; - nsCOMPtr tmp; - - while ( !CanContainTag(parent, aListType)) - { - parent->GetParentNode(getter_AddRefs(tmp)); - if (!tmp) return NS_ERROR_FAILURE; - topChild = parent; - parent = tmp; - } - - if (parent != node) - { - // we need to split up to the child of parent - res = SplitNodeDeep(topChild, node, offset); - if (NS_FAILED(res)) return res; - // topChild already went to the right on the split - // so we don't need to add one to offset when figuring - // out where to plop list - offset = GetIndexOf(parent,topChild); - } - - // make a list - nsCOMPtr newList; - res = CreateNode(aListType, parent, offset, getter_AddRefs(newList)); - if (NS_FAILED(res)) return res; - // make a list item - nsAutoString tag("li"); - nsCOMPtr newItem; - res = CreateNode(tag, newList, 0, getter_AddRefs(newItem)); - if (NS_FAILED(res)) return res; - // put a space in it so layout will draw the list item - // XXX - revisit when layout is fixed - res = selection->Collapse(newItem,0); - if (NS_FAILED(res)) return res; - nsAutoString theText(" "); - res = InsertText(theText); - if (NS_FAILED(res)) return res; - // reposition selection to before the space character - res = GetStartNodeAndOffset(selection, &node, &offset); - if (NS_FAILED(res)) return res; - res = selection->Collapse(node,0); - if (NS_FAILED(res)) return res; - } - - return res; -} - NS_IMETHODIMP nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn) { @@ -1887,7 +1861,6 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * return NS_OK; } - NS_IMETHODIMP nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn) { @@ -2208,144 +2181,6 @@ nsHTMLEditor::CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement* return res; } -NS_IMETHODIMP -nsHTMLEditor::InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection) -{ -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); - - if (mJSEditorLog) - mJSEditorLog->InsertElement(aElement, aDeleteSelection); -#endif // ENABLE_JS_EDITOR_LOG - - nsresult res = NS_ERROR_NOT_INITIALIZED; - - if (!aElement) - return NS_ERROR_NULL_POINTER; - - nsAutoEditBatch beginBatching(this); - // For most elements, set caret after inserting - //PRBool setCaretAfterElement = PR_TRUE; - - nsCOMPtrselection; - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (!NS_SUCCEEDED(res) || !selection) - return NS_ERROR_FAILURE; - - // Clear current selection. - // Should put caret at anchor point? - if (!aDeleteSelection) - { - PRBool collapseAfter = PR_TRUE; - // Named Anchor is a special case, - // We collapse to insert element BEFORE the selection - // For all other tags, we insert AFTER the selection - nsCOMPtr node = do_QueryInterface(aElement); - if (IsNamedAnchorNode(node)) - collapseAfter = PR_FALSE; - - if (collapseAfter) - { - // Default behavior is to collapse to the end of the selection - selection->ClearSelection(); - } else { - // Collapse to the start of the selection, - // We must explore the first range and find - // its parent and starting offset of selection - // TODO: Move this logic to a new method nsIDOMSelection::CollapseToStart()??? - nsCOMPtr firstRange; - res = selection->GetRangeAt(0, getter_AddRefs(firstRange)); - if (NS_SUCCEEDED(res) && firstRange) - { - nsCOMPtr parent; - res = firstRange->GetCommonParent(getter_AddRefs(parent)); - if (NS_SUCCEEDED(res) && parent) - { - PRInt32 startOffset; - firstRange->GetStartOffset(&startOffset); - selection->Collapse(parent, startOffset); - } else { - // Very unlikely, but collapse to the end if we failed above - selection->ClearSelection(); - } - } - } - } - - nsCOMPtr parentSelectedNode; - PRInt32 offsetForInsert; - res = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); - if (NS_SUCCEEDED(res) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetForInsert)) && parentSelectedNode) - { -#ifdef DEBUG_cmanske - { - nsAutoString name; - parentSelectedNode->GetNodeName(name); - printf("InsertElement: Anchor node of selection: "); - wprintf(name.GetUnicode()); - printf(" Offset: %d\n", offsetForInsert); - } -#endif - nsAutoString tagName; - aElement->GetNodeName(tagName); - tagName.ToLowerCase(); - nsCOMPtr parent = parentSelectedNode; - nsCOMPtr topChild = parentSelectedNode; - nsCOMPtr tmp; - nsAutoString parentTagName; - PRBool isRoot; - - // Search up the parent chain to find a suitable container - while (!CanContainTag(parent, tagName)) - { - // If the current parent is a root (body or table cell) - // then go no further - we can't insert - parent->GetNodeName(parentTagName); - res = IsRootTag(parentTagName, isRoot); - if (!NS_SUCCEEDED(res) || isRoot) - return NS_ERROR_FAILURE; - // Get the next parent - parent->GetParentNode(getter_AddRefs(tmp)); - if (!tmp) - return NS_ERROR_FAILURE; - topChild = parent; - parent = tmp; - } -#ifdef DEBUG_cmanske - { - nsAutoString name; - parent->GetNodeName(name); - printf("Parent node to insert under: "); - wprintf(name.GetUnicode()); - printf("\n"); - topChild->GetNodeName(name); - printf("TopChild to split: "); - wprintf(name.GetUnicode()); - printf("\n"); - } -#endif - if (parent != topChild) - { - // we need to split some levels above the original selection parent - res = SplitNodeDeep(topChild, parentSelectedNode, offsetForInsert); - if (NS_FAILED(res)) - return res; - // topChild went to the right on the split - // so this is the offset to insert at - offsetForInsert = GetIndexOf(parent,topChild); - } - // Now we can insert the new node - res = InsertNode(aElement, parent, offsetForInsert); - - // Set caret after element, but check for special case - // of inserting table-related elements: set in first cell instead - if (!SetCaretInTableCell(aElement)) - res = SetCaretAfterElement(aElement); - - } - return res; -} - NS_IMETHODIMP nsHTMLEditor::SaveHLineSettings(nsIDOMElement* aElement) { @@ -2401,7 +2236,7 @@ nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) // DON'T RETURN EXCEPT AT THE END -- WE NEED TO RELEASE THE aAnchorElement if (!aAnchorElement) - goto DELETE_ANCHOR; + goto DELETE_ANCHOR; // DON'T USE GOTO IN C++! // We must have a real selection res = GetSelection(getter_AddRefs(selection)); @@ -2427,7 +2262,7 @@ nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) { nsAutoEditBatch beginBatching(this); const nsString attribute("href"); - SetTextProperty(nsIEditProperty::a, &attribute, &href); + SetInlineProperty(nsIEditProperty::a, &attribute, &href); //TODO: Enumerate through other properties of the anchor tag // and set those as well. // Optimization: Modify SetTextProperty to set all attributes at once? @@ -2443,193 +2278,579 @@ DELETE_ANCHOR: return res; } -PRBool nsHTMLEditor::IsElementInBody(nsIDOMElement* aElement) -{ - if ( aElement ) - { - nsIDOMElement* bodyElement = nsnull; - nsresult res = nsEditor::GetBodyElement(&bodyElement); - if (NS_SUCCEEDED(res) && bodyElement) - { - nsCOMPtr parent; - nsCOMPtr currentElement = do_QueryInterface(aElement); - if (currentElement) - { - do { - currentElement->GetParentNode(getter_AddRefs(parent)); - if (parent) - { - if (parent == bodyElement) - return PR_TRUE; - currentElement = parent; - } - } while(parent); - } - } +NS_IMETHODIMP nsHTMLEditor::SetBackgroundColor(const nsString& aColor) +{ +// nsresult result; + NS_PRECONDITION(mDoc, "Missing Editor DOM Document"); + + // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level + // For initial testing, just set the background on the BODY tag (the document's background) + +// Do this only if setting a table or cell background +// It will be called in nsTextEditor::SetBackgroundColor for the page background +#if 0 //def ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->SetBackgroundColor(aColor); +#endif // ENABLE_JS_EDITOR_LOG + + // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level + // For initial testing, just set the background on the BODY tag (the document's background) + + // Set the background color attribute on the body tag + nsCOMPtr bodyElement; + nsresult res = nsEditor::GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res) && bodyElement) + { + nsAutoEditBatch beginBatching(this); + bodyElement->SetAttribute("bgcolor", aColor); } - return PR_FALSE; + + return res; } -NS_IMETHODIMP -nsHTMLEditor::SelectElement(nsIDOMElement* aElement) +NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsString& aAttribute, const nsString& aValue) { + #ifdef ENABLE_JS_EDITOR_LOG nsAutoJSEditorLogLock logLock(mJSEditorLog); if (mJSEditorLog) - mJSEditorLog->SelectElement(aElement); + mJSEditorLog->SetBodyAttribute(aAttribute, aValue); #endif // ENABLE_JS_EDITOR_LOG - nsresult res = NS_ERROR_NULL_POINTER; + nsresult res; + // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - // Must be sure that element is contained in the document body - if (IsElementInBody(aElement)) + NS_ASSERTION(mDoc, "Missing Editor DOM Document"); + + // Set the background color attribute on the body tag + nsCOMPtr bodyElement; + + res = nsEditor::GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res) && bodyElement) { - nsCOMPtr selection; - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(res) && selection) - { - nsCOMPtrparent; - res = aElement->GetParentNode(getter_AddRefs(parent)); - if (NS_SUCCEEDED(res) && parent) - { - PRInt32 offsetInParent; - res = GetChildOffset(aElement, parent, offsetInParent); - - if (NS_SUCCEEDED(res)) - { - // Collapse selection to just before desired element, - selection->Collapse(parent, offsetInParent); - // then extend it to just after - selection->Extend(parent, offsetInParent+1); - } - } - } + // Use the editor's method which goes through the transaction system + SetAttribute(bodyElement, aAttribute, aValue); } return res; } -PRBool -nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement) +/* +NS_IMETHODIMP nsHTMLEditor::MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection) { - PRBool caretIsSet = PR_FALSE; + return NS_ERROR_NOT_IMPLEMENTED; +} - if (aElement && IsElementInBody(aElement)) +NS_IMETHODIMP nsHTMLEditor::MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::ScrollUp(nsIAtom *aIncrement) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::ScrollDown(nsIAtom *aIncrement) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsHTMLEditor::ScrollIntoView(PRBool aScrollToBegin) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +*/ + + + +NS_IMETHODIMP +nsHTMLEditor::GetDocumentLength(PRInt32 *aCount) +{ + if (!aCount) { return NS_ERROR_NULL_POINTER; } + nsresult result; + // initialize out params + *aCount = 0; + + nsCOMPtr sel; + result = GetSelection(getter_AddRefs(sel)); + if ((NS_SUCCEEDED(result)) && sel) { - nsresult res = NS_OK; - nsAutoString tagName; - aElement->GetNodeName(tagName); - tagName.ToLowerCase(); - if (tagName == "table" || tagName == "tr" || - tagName == "td" || tagName == "th" || - tagName == "thead" || tagName == "tfoot" || - tagName == "tbody" || tagName == "caption") + nsAutoSelectionReset selectionResetter(sel); + result = SelectAll(); + if (NS_SUCCEEDED(result)) { - nsCOMPtr node = do_QueryInterface(aElement); - nsCOMPtr parent; - // This MUST succeed if IsElementInBody was TRUE - node->GetParentNode(getter_AddRefs(parent)); - nsCOMPtrfirstChild; - // Find deepest child - PRBool hasChild; - while (NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild) + PRInt32 start, end; + result = GetTextSelectionOffsets(sel, start, end); + if (NS_SUCCEEDED(result)) { - if (NS_SUCCEEDED(node->GetFirstChild(getter_AddRefs(firstChild)))) - { - parent = node; - node = firstChild; + NS_ASSERTION(0==start, "GetTextSelectionOffsets failed to set start correctly."); + NS_ASSERTION(0<=end, "GetTextSelectionOffsets failed to set end correctly."); + if (0<=end) { + *aCount = end; } } - PRInt32 offset = 0; - nsCOMPtrlastChild; - res = parent->GetLastChild(getter_AddRefs(lastChild)); - if (NS_SUCCEEDED(res) && lastChild && node != lastChild) - { - if (node == lastChild) - { - // Check if node is text and has more than just a   - nsCOMPtrtextNode = do_QueryInterface(node); - nsString text; - char nbspStr[2] = {nbsp, 0}; - if (textNode && textNode->GetData(text)) - { - // Set selection relative to the text node - parent = node; - PRInt32 len = text.Length(); - if (len > 1 || text != nbspStr) - { - offset = len; - } - } - } else { - // We have > 1 node, so set to end of content - } - } - // Set selection at beginning of deepest node - // Should we set - nsCOMPtr selection; - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(res) && selection) - { - res = selection->Collapse(parent, offset); - if (NS_SUCCEEDED(res)) - caretIsSet = PR_TRUE; - } } } - return caretIsSet; -} + return result; +} -NS_IMETHODIMP -nsHTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement) +NS_IMETHODIMP nsHTMLEditor::SetMaxTextLength(PRInt32 aMaxTextLength) { -#ifdef ENABLE_JS_EDITOR_LOG - nsAutoJSEditorLogLock logLock(mJSEditorLog); + mMaxTextLength = aMaxTextLength; + return NS_OK; +} - if (mJSEditorLog) - mJSEditorLog->SetCaretAfterElement(aElement); -#endif // ENABLE_JS_EDITOR_LOG +NS_IMETHODIMP nsHTMLEditor::GetMaxTextLength(PRInt32& aMaxTextLength) +{ + aMaxTextLength = mMaxTextLength; + return NS_OK; +} - nsresult res = NS_ERROR_NULL_POINTER; - // Be sure the element is contained in the document body - if (aElement && IsElementInBody(aElement)) - { - nsCOMPtr selection; - res = nsEditor::GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(res) && selection) - { - nsCOMPtrparent; - res = aElement->GetParentNode(getter_AddRefs(parent)); - if (NS_SUCCEEDED(res) && parent) - { - PRInt32 offsetInParent; - res = GetChildOffset(aElement, parent, offsetInParent); - if (NS_SUCCEEDED(res)) - { - // Collapse selection to just after desired element, - selection->Collapse(parent, offsetInParent+1); -#ifdef DEBUG_cmanske - { - nsAutoString name; - parent->GetNodeName(name); - printf("SetCaretAfterElement: Parent node: "); - wprintf(name.GetUnicode()); - printf(" Offset: %d\n\nHTML:\n", offsetInParent+1); - nsString Format("text/html"); - nsString ContentsAs; - OutputToString(ContentsAs, Format, 2); - wprintf(ContentsAs.GetUnicode()); - } +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorStyleSheets methods --- +#pragma mark - #endif + + +NS_IMETHODIMP +nsHTMLEditor::AddStyleSheet(nsICSSStyleSheet* aSheet) +{ + AddStyleSheetTxn* aTxn; + nsresult rv = CreateTxnForAddStyleSheet(aSheet, &aTxn); + if (NS_SUCCEEDED(rv) && aTxn) + { + rv = Do(aTxn); + if (NS_SUCCEEDED(rv)) + { + mLastStyleSheet = do_QueryInterface(aSheet); // save it so we can remove before applying the next one + } + } + + return rv; +} + + +NS_IMETHODIMP +nsHTMLEditor::RemoveStyleSheet(nsICSSStyleSheet* aSheet) +{ + RemoveStyleSheetTxn* aTxn; + nsresult rv = CreateTxnForRemoveStyleSheet(aSheet, &aTxn); + if (NS_SUCCEEDED(rv) && aTxn) + { + rv = Do(aTxn); + if (NS_SUCCEEDED(rv)) + { + mLastStyleSheet = nsnull; // forget it + } + } + + return rv; +} + + +NS_IMETHODIMP nsHTMLEditor::ApplyStyleSheet(const nsString& aURL) +{ +#ifdef ENABLE_JS_EDITOR_LOG + nsAutoJSEditorLogLock logLock(mJSEditorLog); + + if (mJSEditorLog) + mJSEditorLog->ApplyStyleSheet(aURL); +#endif // ENABLE_JS_EDITOR_LOG + + // XXX: Note that this is not an undo-able action yet! + + nsresult rv = NS_OK; + nsIURI* uaURL = 0; + +#ifndef NECKO + rv = NS_NewURL(&uaURL, aURL); +#else + rv = NS_NewURI(&uaURL, aURL); +#endif // NECKO + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr document; + + rv = mPresShell->GetDocument(getter_AddRefs(document)); + + if (NS_SUCCEEDED(rv)) { + if (document) { + nsCOMPtr container = do_QueryInterface(document); + + if (container) { + nsICSSLoader *cssLoader = 0; + nsICSSStyleSheet *cssStyleSheet = 0; + + rv = container->GetCSSLoader(cssLoader); + + if (NS_SUCCEEDED(rv)) { + if (cssLoader) { + PRBool complete; + + rv = cssLoader->LoadAgentSheet(uaURL, cssStyleSheet, complete, + ApplyStyleSheetToPresShellDocument, + this); + + if (NS_SUCCEEDED(rv)) { + if (complete) { + if (cssStyleSheet) { + ApplyStyleSheetToPresShellDocument(cssStyleSheet, + this); + } + else + rv = NS_ERROR_NULL_POINTER; + } + + // + // If not complete, we will be notified later + // with a call to AddStyleSheetToEditorDocument(). + // + } + } + else + rv = NS_ERROR_NULL_POINTER; + } } + else + rv = NS_ERROR_NULL_POINTER; } + else + rv = NS_ERROR_NULL_POINTER; } + + NS_RELEASE(uaURL); } + + return rv; +} + + +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsIEditorMailSupport methods --- +#pragma mark - +#endif + +// +// Get the wrap width for the first PRE tag in the document. +// If no PRE tag, throw an error. +// +NS_IMETHODIMP nsHTMLEditor::GetBodyWrapWidth(PRInt32 *aWrapColumn) +{ + nsresult res; + + if (! aWrapColumn) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr preElement = FindPreElement(); + if (!preElement) + return NS_ERROR_UNEXPECTED; + nsString colsStr ("cols"); + nsString numCols; + PRBool isSet; + res = GetAttributeValue(preElement, colsStr, numCols, isSet); + if (!NS_SUCCEEDED(res)) + return NS_ERROR_UNEXPECTED; + + if (isSet) + { + PRInt32 errCode; + *aWrapColumn = numCols.ToInteger(&errCode); + if (errCode) + return NS_ERROR_FAILURE; + return NS_OK; + } + + // if we get here, cols isn't set, so check whether wrap is set: + nsString wrapStr ("wrap"); + res = GetAttributeValue(preElement, colsStr, numCols, isSet); + if (!NS_SUCCEEDED(res)) + return NS_ERROR_UNEXPECTED; + + if (isSet) + *aWrapColumn = 0; // wrap to window width + else + *aWrapColumn = -1; // no wrap + + return NS_OK; +} + +// +// Change the wrap width on the first
 tag in this document.
+// (Eventually want to search for more than one in case there are
+// interspersed quoted text blocks.)
+// 
+NS_IMETHODIMP nsHTMLEditor::SetBodyWrapWidth(PRInt32 aWrapColumn)
+{
+  nsresult res;
+
+  mWrapColumn = aWrapColumn;
+
+  nsCOMPtr preElement = FindPreElement();
+  if (!preElement)
+    return NS_ERROR_UNEXPECTED;
+
+  nsString wrapStr ("wrap");
+  nsString colsStr ("cols");
+
+  // If wrap col is nonpositive, then we need to remove any existing "cols":
+  if (aWrapColumn <= 0)
+  {
+    (void)RemoveAttribute(preElement, colsStr);
+
+    if (aWrapColumn == 0)        // Wrap to window width
+    {
+      nsString oneStr ("1");
+      res = SetAttribute(preElement, wrapStr, oneStr);
+    }
+    else res = NS_OK;
+    return res;
+  }
+
+  // Otherwise we're setting cols, want to remove wrap
+  (void)RemoveAttribute(preElement, wrapStr);
+  nsString numCols;
+  numCols.Append(aWrapColumn, 10);
+  res = SetAttribute(preElement, colsStr, numCols);
+
+  // Layout doesn't detect that this attribute change requires redraw.  Sigh.
+  //HACKForceRedraw();  // This doesn't do it either!
+
+  return res;
+}  
+
+
+
+// 
+// HTML PasteAsQuotation: Paste in a blockquote type=cite
+//
+NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->PasteAsQuotation();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsAutoString citation("");
+  return PasteAsCitedQuotation(citation);
+}
+
+NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation)
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->PasteAsCitedQuotation(aCitation);
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsAutoEditBatch beginBatching(this);
+  nsCOMPtr newNode;
+  nsAutoString tag("blockquote");
+  nsresult res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+  if (NS_FAILED(res) || !newNode)
+    return res;
+
+  // Try to set type=cite.  Ignore it if this fails.
+  nsCOMPtr newElement (do_QueryInterface(newNode));
+  if (newElement)
+  {
+    nsAutoString type ("type");
+    nsAutoString cite ("cite");
+    newElement->SetAttribute(type, cite);
+  }
+
+  // Set the selection to the underneath the node we just inserted:
+  nsCOMPtr selection;
+  res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res) || !selection)
+  {
+#ifdef DEBUG_akkana
+    printf("Can't get selection!");
+#endif
+  }
+
+  res = selection->Collapse(newNode, 0);
+  if (NS_FAILED(res))
+  {
+#ifdef DEBUG_akkana
+    printf("Couldn't collapse");
+#endif
+  }
+
+  res = Paste();
   return res;
 }
 
+//
+// Paste a plaintext quotation
+//
+NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->PasteAsQuotation();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  // Get Clipboard Service
+  nsIClipboard* clipboard;
+  nsresult rv = nsServiceManager::GetService(kCClipboardCID,
+                                             nsIClipboard::GetIID(),
+                                             (nsISupports **)&clipboard);
+
+  // Create generic Transferable for getting the data
+  nsCOMPtr trans;
+  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
+                                          nsITransferable::GetIID(), 
+                                          (void**) getter_AddRefs(trans));
+  if (NS_SUCCEEDED(rv) && trans)
+  {
+    // We only handle plaintext pastes here
+    nsAutoString flavor(kTextMime);
+    trans->AddDataFlavor(&flavor);
+
+      // Get the Data from the clipboard
+    clipboard->GetData(trans);
+
+    // Now we ask the transferable for the data
+    // it still owns the data, we just have a pointer to it.
+    // If it can't support a "text" output of the data the call will fail
+    char *str = 0;
+    PRUint32 len;
+    rv = trans->GetTransferData(&flavor, (void **)&str, &len);
+    if (NS_SUCCEEDED(rv) && str && len > 0)
+    {
+      nsString stuffToPaste;
+      stuffToPaste.SetString(str, len);
+      rv = InsertAsPlaintextQuotation(stuffToPaste);
+    }
+  }
+  nsServiceManager::ReleaseService(kCClipboardCID, clipboard);
+
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText)
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->InsertAsQuotation(aQuotedText);
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsAutoString citation ("");
+  return InsertAsCitedQuotation(aQuotedText, citation);
+}
+
+// text insert.
+NS_IMETHODIMP nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText)
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->InsertAsQuotation(aQuotedText);
+#endif // ENABLE_JS_EDITOR_LOG
+
+  // Now we have the text.  Cite it appropriately:
+  nsCOMPtr citer;
+  nsCOMPtr prefs;
+  nsresult rv = nsServiceManager::GetService(kPrefServiceCID,
+                                             nsIPref::GetIID(),
+                                             (nsISupports**)&prefs);
+  char *citationType = 0;
+  rv = prefs->CopyCharPref("mail.compose.citationType", &citationType);
+                          
+  if (NS_SUCCEEDED(rv) && citationType[0])
+  {
+    if (!strncmp(citationType, "aol", 3))
+      citer = new nsAOLCiter;
+    else
+      citer = new nsInternetCiter;
+    PL_strfree(citationType);
+  }
+  else
+    citer = new nsInternetCiter;
+  
+  nsServiceManager::ReleaseService(kPrefServiceCID, prefs);
+
+  // Let the citer quote it for us:
+  nsString quotedStuff;
+  rv = citer->GetCiteString(aQuotedText, quotedStuff);
+  if (!NS_SUCCEEDED(rv))
+    return rv;
+
+  // Insert blank lines after the quoted text:
+  quotedStuff += "\n\n";
+
+  return InsertText(quotedStuff);
+}
+
+NS_IMETHODIMP nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
+                                                   const nsString& aCitation)
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->InsertAsCitedQuotation(aQuotedText, aCitation);
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsAutoEditBatch beginBatching(this);
+  nsCOMPtr newNode;
+  nsAutoString tag("blockquote");
+  nsresult res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+  if (NS_FAILED(res) || !newNode)
+    return res;
+
+  // Try to set type=cite.  Ignore it if this fails.
+  nsCOMPtr newElement (do_QueryInterface(newNode));
+  if (newElement)
+  {
+    nsAutoString type ("type");
+    nsAutoString cite ("cite");
+    newElement->SetAttribute(type, cite);
+
+    if (aCitation.Length() > 0)
+      newElement->SetAttribute(cite, aCitation);
+  }
+
+  res = InsertHTML(aQuotedText);
+  return res;
+}
+
+
 NS_IMETHODIMP
 nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
 {
@@ -2702,6 +2923,1368 @@ nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
   return res;
 }
 
+
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark --- nsIEditor overrides ---
+#pragma mark -
+#endif
+
+NS_IMETHODIMP nsHTMLEditor::Cut()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->Cut();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsCOMPtr selection;
+  nsresult res = mPresShell->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection));
+  if (!NS_SUCCEEDED(res))
+    return res;
+
+  PRBool isCollapsed;
+  if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  res = Copy();
+  if (NS_SUCCEEDED(res))
+    res = DeleteSelection(eDoNothing);
+  return res;
+}
+
+NS_IMETHODIMP nsHTMLEditor::Copy()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->Copy();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  //printf("nsEditor::Copy\n");
+
+  return mPresShell->DoCopy();
+}
+
+NS_IMETHODIMP nsHTMLEditor::Paste()
+{
+#ifdef ENABLE_JS_EDITOR_LOG
+  nsAutoJSEditorLogLock logLock(mJSEditorLog);
+
+  if (mJSEditorLog)
+    mJSEditorLog->Paste();
+#endif // ENABLE_JS_EDITOR_LOG
+
+  nsString stuffToPaste;
+
+  // Get Clipboard Service
+  nsIClipboard* clipboard;
+  nsresult rv = nsServiceManager::GetService(kCClipboardCID,
+                                             nsIClipboard::GetIID(),
+                                             (nsISupports **)&clipboard);
+
+  // Create generic Transferable for getting the data
+  nsCOMPtr trans;
+  rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, 
+                                          nsITransferable::GetIID(), 
+                                          (void**) getter_AddRefs(trans));
+  if (NS_SUCCEEDED(rv))
+  {
+    // Get the nsITransferable interface for getting the data from the clipboard
+    if (trans)
+    {
+      // Create the desired DataFlavor for the type of data
+      // we want to get out of the transferable
+      nsAutoString imageFlavor(kJPEGImageMime);
+      nsAutoString htmlFlavor(kHTMLMime);
+      nsAutoString textFlavor(kTextMime);
+
+      if ((mFlags & eEditorPlaintextMask) == 0)  // This should only happen in html editors, not plaintext
+      {
+        trans->AddDataFlavor(&imageFlavor);
+        trans->AddDataFlavor(&htmlFlavor);
+      }
+      trans->AddDataFlavor(&textFlavor);
+
+      // Get the Data from the clipboard
+      if (NS_SUCCEEDED(clipboard->GetData(trans)))
+      {
+        nsAutoString flavor;
+        char *       data;
+        PRUint32     len;
+        if (NS_SUCCEEDED(trans->GetAnyTransferData(&flavor, (void **)&data, &len)))
+        {
+#ifdef DEBUG_akkana
+          printf("Got flavor [%s]\n", flavor.ToNewCString());
+#endif
+          if (flavor.Equals(htmlFlavor))
+          {
+            if (data && len > 0) // stuffToPaste is ready for insertion into the content
+            {
+              stuffToPaste.SetString(data, len);
+              rv = InsertHTML(stuffToPaste);
+            }
+          }
+          else if (flavor.Equals(textFlavor))
+          {
+            if (data && len > 0) // stuffToPaste is ready for insertion into the content
+            {
+              stuffToPaste.SetString(data, len);
+              rv = InsertText(stuffToPaste);
+            }
+          }
+          else if (flavor.Equals(imageFlavor))
+          {
+            // Insert Image code here
+            printf("Don't know how to insert an image yet!\n");
+            //nsIImage* image = (nsIImage *)data;
+            //NS_RELEASE(image);
+            rv = NS_ERROR_FAILURE; // for now give error code
+          }
+        }
+
+      }
+    }
+  }
+  nsServiceManager::ReleaseService(kCClipboardCID, clipboard);
+
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::OutputToString(nsString& aOutputString,
+                                           const nsString& aFormatType,
+                                           PRUint32 aFlags)
+{
+  PRBool cancel;
+  nsString resultString;
+  nsTextRulesInfo ruleInfo(nsTextEditRules::kOutputText);
+  ruleInfo.outString = &resultString;
+  nsresult rv = mRules->WillDoAction(nsnull, &ruleInfo, &cancel);
+  if (NS_FAILED(rv)) { return rv; }
+  if (PR_TRUE==cancel)
+  { // this case will get triggered by password fields
+    aOutputString = *(ruleInfo.outString);
+  }
+  else
+  { // default processing
+    nsCOMPtr encoder;
+    char* progid = new char[strlen(NS_DOC_ENCODER_PROGID_BASE) + aFormatType.Length() + 1];
+    if (! progid)
+      return NS_ERROR_OUT_OF_MEMORY;
+    strcpy(progid, NS_DOC_ENCODER_PROGID_BASE);
+    char* type = aFormatType.ToNewCString();
+    strcat(progid, type);
+    delete[] type;
+    rv = nsComponentManager::CreateInstance(progid,
+                                            nsnull,
+                                            nsIDocumentEncoder::GetIID(),
+                                            getter_AddRefs(encoder));
+
+    delete[] progid;
+    if (NS_FAILED(rv))
+    {
+      printf("Couldn't get progid %s\n", progid);
+      return rv;
+    }
+
+    nsCOMPtr domdoc;
+    rv = GetDocument(getter_AddRefs(domdoc));
+    if (NS_FAILED(rv))
+      return rv;
+    nsCOMPtr doc = do_QueryInterface(domdoc);
+
+    nsCOMPtr shell;
+
+ 	  rv = GetPresShell(getter_AddRefs(shell));
+    if (NS_FAILED(rv))
+      return rv;
+    rv = encoder->Init(shell, doc, aFormatType);
+    if (NS_FAILED(rv))
+      return rv;
+
+	  if (aFlags & EditorOutputSelectionOnly)
+    {
+	    nsCOMPtr selection;
+	    rv = GetSelection(getter_AddRefs(selection));
+	    if (NS_SUCCEEDED(rv) && selection)
+	      encoder->SetSelection(selection);
+	  }
+	  
+    // Try to set pretty printing, but don't panic if it doesn't work:
+    (void)encoder->PrettyPrint((aFlags & EditorOutputFormatted)
+                               ? PR_TRUE : PR_FALSE);
+    // Indicate whether we want the comment and doctype headers prepended:
+    (void)encoder->AddHeader((aFlags & EditorOutputNoDoctype)
+                             ? PR_FALSE : PR_TRUE);
+    // Set the wrap column.  If our wrap column is 0,
+    // i.e. wrap to body width, then don't set it, let the
+    // document encoder use its own default.
+    if (mWrapColumn != 0)
+    {
+      PRUint32 wc;
+      if (mWrapColumn < 0)
+        wc = 0;
+      else
+        wc = (PRUint32)mWrapColumn;
+      if (mWrapColumn > 0)
+        (void)encoder->SetWrapColumn(wc);
+    }
+
+    rv = encoder->EncodeToString(aOutputString);
+  }
+  return rv;
+}
+
+NS_IMETHODIMP nsHTMLEditor::OutputToStream(nsIOutputStream* aOutputStream,
+                                           const nsString& aFormatType,
+                                           const nsString* aCharset,
+                                           PRUint32 aFlags)
+{
+  nsresult rv;
+  nsCOMPtr encoder;
+  char* progid = new char[strlen(NS_DOC_ENCODER_PROGID_BASE) + aFormatType.Length() + 1];
+  if (! progid)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+  strcpy(progid, NS_DOC_ENCODER_PROGID_BASE);
+  char* type = aFormatType.ToNewCString();
+  strcat(progid, type);
+  delete[] type;
+  rv = nsComponentManager::CreateInstance(progid,
+                                          nsnull,
+                                          nsIDocumentEncoder::GetIID(),
+                                          getter_AddRefs(encoder));
+
+  delete[] progid;
+  if (NS_FAILED(rv))
+  {
+    printf("Couldn't get progid %s\n", progid);
+    return rv;
+  }
+
+  nsCOMPtr domdoc;
+  rv = GetDocument(getter_AddRefs(domdoc));
+  if (NS_FAILED(rv))
+    return rv;
+  nsCOMPtr doc = do_QueryInterface(domdoc);
+
+  if (aCharset && aCharset->Length() != 0 && aCharset->Equals("null")==PR_FALSE)
+    encoder->SetCharset(*aCharset);
+
+  nsCOMPtr shell;
+
+ 	rv = GetPresShell(getter_AddRefs(shell));
+  if (NS_SUCCEEDED(rv)) {
+    rv = encoder->Init(shell,doc, aFormatType);
+    if (NS_FAILED(rv))
+      return rv;
+  }
+
+  if (aFlags & EditorOutputSelectionOnly)
+  {
+    nsCOMPtr  selection;
+    rv = GetSelection(getter_AddRefs(selection));
+    if (NS_SUCCEEDED(rv) && selection)
+      encoder->SetSelection(selection);
+  }
+
+  // Try to set pretty printing, but don't panic if it doesn't work:
+  (void)encoder->PrettyPrint((aFlags & EditorOutputFormatted)
+                             ? PR_TRUE : PR_FALSE);
+  // Indicate whether we want the comment and doc type headers prepended:
+  (void)encoder->AddHeader((aFlags & EditorOutputNoDoctype)
+                           ? PR_FALSE : PR_TRUE);
+  // Set the wrap column.  If our wrap column is 0,
+  // i.e. wrap to body width, then don't set it, let the
+  // document encoder use its own default.
+  if (mWrapColumn != 0)
+  {
+    PRUint32 wc;
+    if (mWrapColumn < 0)
+      wc = 0;
+    else
+      wc = (PRUint32)mWrapColumn;
+    if (mWrapColumn > 0)
+      (void)encoder->SetWrapColumn(wc);
+  }
+
+  return encoder->EncodeToStream(aOutputStream);
+}
+
+
+NS_IMETHODIMP
+nsHTMLEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
+{
+#ifdef DEBUG
+  if (!outNumTests || !outNumTestsFailed)
+    return NS_ERROR_NULL_POINTER;
+
+	TextEditorTest *tester = new TextEditorTest();
+	if (!tester)
+	  return NS_ERROR_OUT_OF_MEMORY;
+	 
+  tester->Run(this, outNumTests, outNumTestsFailed);
+  delete tester;
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark --- StyleSheet utils ---
+#pragma mark -
+#endif
+
+
+NS_IMETHODIMP
+nsHTMLEditor::ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet)
+{
+  nsresult  rv = NS_OK;
+  
+  BeginTransaction();
+
+  if (mLastStyleSheet)
+  {
+    rv = RemoveStyleSheet(mLastStyleSheet);
+  }
+
+  rv = AddStyleSheet(aNewSheet);
+  
+  EndTransaction();
+
+  return rv;
+}
+
+
+
+/* static callback */
+void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData)
+{
+  nsresult rv = NS_OK;
+
+  nsHTMLEditor *editor = NS_STATIC_CAST(nsHTMLEditor*, aData);
+  if (editor)
+  {
+    rv = editor->ReplaceStyleSheet(aSheet);
+  }
+  
+  // we lose the return value here. Set a flag in the editor?
+}
+
+
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark --- Random methods ---
+#pragma mark -
+#endif
+
+
+NS_IMETHODIMP nsHTMLEditor::GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject)
+{
+  nsresult result = NS_ERROR_FAILURE;  // we return an error unless we get the index
+  if( mPresShell != nsnull )
+  {
+    if ((nsnull!=aNode))
+    { // get the content interface
+      nsCOMPtr nodeAsContent( do_QueryInterface(aNode) );
+      if (nodeAsContent)
+      { // get the frame from the content interface
+        //Note: frames are not ref counted, so don't use an nsCOMPtr
+        *aLayoutObject = nsnull;
+        result = mPresShell->GetLayoutObjectFor(nodeAsContent, aLayoutObject);
+      }
+    }
+    else {
+      result = NS_ERROR_NULL_POINTER;
+    }
+  }
+  return result;
+}
+
+
+NS_IMETHODIMP
+nsHTMLEditor::SetTypeInStateForProperty(TypeInState    &aTypeInState, 
+                                        nsIAtom        *aPropName, 
+                                        const nsString *aAttribute,
+                                        const nsString *aValue)
+{
+  if (!aPropName) {
+    return NS_ERROR_NULL_POINTER;
+  }
+  PRUint32 propEnum;
+  aTypeInState.GetEnumForName(aPropName, propEnum);
+  if (nsIEditProperty::b==aPropName || nsIEditProperty::i==aPropName || nsIEditProperty::u==aPropName) 
+  {
+    if (PR_TRUE==aTypeInState.IsSet(propEnum))
+    { // toggle currently set boldness
+      aTypeInState.UnSet(propEnum);
+    }
+    else
+    { // get the current style and set boldness to the opposite of the current state
+      PRBool any = PR_FALSE;
+      PRBool all = PR_FALSE;
+      PRBool first = PR_FALSE;
+      GetInlineProperty(aPropName, aAttribute, nsnull, first, any, all); // operates on current selection
+      aTypeInState.SetProp(propEnum, !any);
+    }
+  }
+  else if (nsIEditProperty::font==aPropName)
+  {
+    if (!aAttribute) { return NS_ERROR_NULL_POINTER; }
+    nsIAtom *attribute = NS_NewAtom(*aAttribute);
+    if (!attribute) { return NS_ERROR_NULL_POINTER; }
+    PRUint32 attrEnum;
+    aTypeInState.GetEnumForName(attribute, attrEnum);
+    if (nsIEditProperty::color==attribute || nsIEditProperty::face==attribute || nsIEditProperty::size==attribute)
+    {
+      if (PR_TRUE==aTypeInState.IsSet(attrEnum))
+      { 
+        if (nsnull==aValue) {
+          aTypeInState.UnSet(attrEnum);
+        }
+        else { // we're just changing the value of color
+          aTypeInState.SetPropValue(attrEnum, *aValue);
+        }
+      }
+      else
+      { // get the current style and set font color if it's needed
+        if (!aValue) { return NS_ERROR_NULL_POINTER; }
+        PRBool any = PR_FALSE;
+        PRBool all = PR_FALSE;
+        PRBool first = PR_FALSE;
+        GetInlineProperty(aPropName, aAttribute, aValue, first, any, all); // operates on current selection
+        if (PR_FALSE==all) {
+          aTypeInState.SetPropValue(attrEnum, *aValue);
+        }
+      }    
+    }
+    else { return NS_ERROR_FAILURE; }
+  }
+  else { return NS_ERROR_FAILURE; }
+  return NS_OK;
+}
+
+
+// this will NOT find aAttribute unless aAttribute has a non-null value
+// so singleton attributes like 
will not be matched! +void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aIsSet, + nsIDOMNode **aStyleNode) const +{ + nsresult result; + aIsSet = PR_FALSE; // must be initialized to false for code below to work + nsAutoString propName; + aProperty->ToString(propName); + nsCOMPtrparent; + result = aNode->GetParentNode(getter_AddRefs(parent)); + while (NS_SUCCEEDED(result) && parent) + { + nsCOMPtrelement; + element = do_QueryInterface(parent); + if (element) + { + nsString tag; + element->GetTagName(tag); + if (propName.EqualsIgnoreCase(tag)) + { + PRBool found = PR_FALSE; + if (aAttribute && 0!=aAttribute->Length()) + { + nsAutoString value; + element->GetAttribute(*aAttribute, value); + if (0!=value.Length()) + { + if (!aValue) { + found = PR_TRUE; + } + else if (aValue->EqualsIgnoreCase(value)) { + found = PR_TRUE; + } + else { // we found the prop with the attribute, but the value doesn't match + break; + } + } + } + else { + found = PR_TRUE; + } + if (PR_TRUE==found) + { + aIsSet = PR_TRUE; + break; + } + } + } + nsCOMPtrtemp; + result = parent->GetParentNode(getter_AddRefs(temp)); + if (NS_SUCCEEDED(result) && temp) { + parent = do_QueryInterface(temp); + } + else { + parent = do_QueryInterface(nsnull); + } + } +} + +void nsHTMLEditor::IsTextStyleSet(nsIStyleContext *aSC, + nsIAtom *aProperty, + const nsString *aAttribute, + PRBool &aIsSet) const +{ + aIsSet = PR_FALSE; + if (aSC && aProperty) + { + nsStyleFont* font = (nsStyleFont*)aSC->GetStyleData(eStyleStruct_Font); + if (nsIEditProperty::i==aProperty) + { + aIsSet = PRBool(font->mFont.style & NS_FONT_STYLE_ITALIC); + } + else if (nsIEditProperty::b==aProperty) + { // XXX: check this logic with Peter + aIsSet = PRBool(font->mFont.weight > NS_FONT_WEIGHT_NORMAL); + } + } +} + + +// this is a complete ripoff from nsTextEditor::GetTextSelectionOffsetsForRange +// the two should use common code, or even just be one method +nsresult nsHTMLEditor::GetTextSelectionOffsets(nsIDOMSelection *aSelection, + PRInt32 &aStartOffset, + PRInt32 &aEndOffset) +{ + nsresult result; + // initialize out params + aStartOffset = 0; // default to first char in selection + aEndOffset = -1; // default to total length of text in selection + + nsCOMPtr startNode, endNode, parentNode; + PRInt32 startOffset, endOffset; + aSelection->GetAnchorNode(getter_AddRefs(startNode)); + aSelection->GetAnchorOffset(&startOffset); + aSelection->GetFocusNode(getter_AddRefs(endNode)); + aSelection->GetFocusOffset(&endOffset); + + nsCOMPtr enumerator; + result = aSelection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + // don't use "result" in this block + enumerator->First(); + nsCOMPtr currentItem; + nsresult findParentResult = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(findParentResult)) && (currentItem)) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + range->GetCommonParent(getter_AddRefs(parentNode)); + } + else parentNode = do_QueryInterface(startNode); + } + + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), + getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + PRUint32 totalLength=0; + nsCOMPtrtextNode; + nsCOMPtrblockParentContent = do_QueryInterface(parentNode); + iter->Init(blockParentContent); + // loop through the content iterator for each content node + nsCOMPtr content; + result = iter->CurrentNode(getter_AddRefs(content)); + while (NS_COMFALSE == iter->IsDone()) + { + textNode = do_QueryInterface(content); + if (textNode) + { + nsCOMPtrcurrentNode = do_QueryInterface(textNode); + if (!currentNode) {return NS_ERROR_NO_INTERFACE;} + if (PR_TRUE==IsEditable(currentNode)) + { + if (currentNode.get() == startNode.get()) + { + aStartOffset = totalLength + startOffset; + } + if (currentNode.get() == endNode.get()) + { + aEndOffset = totalLength + endOffset; + break; + } + PRUint32 length; + textNode->GetLength(&length); + totalLength += length; + } + } + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + if (-1==aEndOffset) { + aEndOffset = totalLength; + } + } + return result; +} + + + +void nsHTMLEditor::GetTextSelectionOffsetsForRange(nsIDOMSelection *aSelection, + nsIDOMNode **aParent, + PRInt32 &aStartOffset, + PRInt32 &aEndOffset) +{ + nsresult result; + + nsCOMPtr startNode, endNode; + PRInt32 startOffset, endOffset; + aSelection->GetAnchorNode(getter_AddRefs(startNode)); + aSelection->GetAnchorOffset(&startOffset); + aSelection->GetFocusNode(getter_AddRefs(endNode)); + aSelection->GetFocusOffset(&endOffset); + + nsCOMPtr enumerator; + result = aSelection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(result) && enumerator) + { + enumerator->First(); + nsCOMPtr currentItem; + result = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(result)) && currentItem) + { + nsCOMPtr range( do_QueryInterface(currentItem) ); + range->GetCommonParent(aParent); + } + } + + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), + getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + PRUint32 totalLength=0; + nsCOMPtrtextNode; + nsCOMPtrblockParentContent = do_QueryInterface(*aParent); + iter->Init(blockParentContent); + // loop through the content iterator for each content node + nsCOMPtr content; + result = iter->CurrentNode(getter_AddRefs(content)); + while (NS_COMFALSE == iter->IsDone()) + { + textNode = do_QueryInterface(content); + if (textNode) + { + nsCOMPtrcurrentNode = do_QueryInterface(textNode); + if (currentNode.get() == startNode.get()) + { + aStartOffset = totalLength + startOffset; + } + if (currentNode.get() == endNode.get()) + { + aEndOffset = totalLength + endOffset; + break; + } + PRUint32 length; + textNode->GetLength(&length); + totalLength += length; + } + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + } +} + +void nsHTMLEditor::ResetTextSelectionForRange(nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIDOMSelection *aSelection) +{ + nsCOMPtr startNode, endNode; + PRInt32 startOffset, endOffset; + + nsresult result; + nsCOMPtr iter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), + getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + PRBool setStart = PR_FALSE; + PRUint32 totalLength=0; + nsCOMPtrtextNode; + nsCOMPtrblockParentContent = do_QueryInterface(aParent); + iter->Init(blockParentContent); + // loop through the content iterator for each content node + nsCOMPtr content; + result = iter->CurrentNode(getter_AddRefs(content)); + while (NS_COMFALSE == iter->IsDone()) + { + textNode = do_QueryInterface(content); + if (textNode) + { + PRUint32 length; + textNode->GetLength(&length); + if ((PR_FALSE==setStart) && aStartOffset<=(PRInt32)(totalLength+length)) + { + setStart = PR_TRUE; + startNode = do_QueryInterface(textNode); + startOffset = aStartOffset-totalLength; + } + if (aEndOffset<=(PRInt32)(totalLength+length)) + { + endNode = do_QueryInterface(textNode); + endOffset = aEndOffset-totalLength; + break; + } + totalLength += length; + } + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + aSelection->Collapse(startNode, startOffset); + aSelection->Extend(endNode, endOffset); + } +} + +#pragma mark - + +//================================================================ +// HTML Editor methods +// +// Note: Table Editing methods are implemented in EditTable.cpp +// + +NS_IMETHODIMP +nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, + nsString &aParentTag, + BlockTransformationType aTransformation) +{ + if (!aNode) { return NS_ERROR_NULL_POINTER; } + if (gNoisy) + { + char *tag = aParentTag.ToNewCString(); + printf("---------- ReParentContentOfNode(%p,%s,%d) -----------\n", aNode, tag, aTransformation); + delete [] tag; + } + // find the current block parent, or just use aNode if it is a block node + nsCOMPtrblockParentElement; + nsCOMPtrnodeToReParent; // this is the node we'll operate on, by default it's aNode + nsresult res = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(nodeToReParent)); + PRBool nodeIsInline; + PRBool nodeIsBlock=PR_FALSE; + IsNodeInline(aNode, nodeIsInline); + if (PR_FALSE==nodeIsInline) + { + nsresult QIResult; + nsCOMPtrnodeAsText; + QIResult = aNode->QueryInterface(nsIDOMCharacterData::GetIID(), getter_AddRefs(nodeAsText)); + if (NS_FAILED(QIResult) || !nodeAsText) { + nodeIsBlock=PR_TRUE; + } + } + // if aNode is the block parent, then the node to reparent is one of its children + if (PR_TRUE==nodeIsBlock) + { + res = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(blockParentElement)); + if (NS_SUCCEEDED(res) && blockParentElement) { + res = aNode->GetFirstChild(getter_AddRefs(nodeToReParent)); + } + } + else { // we just need to get the block parent of aNode + res = GetBlockParent(aNode, getter_AddRefs(blockParentElement)); + } + // at this point, we must have a good res, a node to reparent, and a block parent + if (!nodeToReParent) { return NS_ERROR_UNEXPECTED;} + if (!blockParentElement) { return NS_ERROR_NULL_POINTER;} + if (NS_SUCCEEDED(res)) + { + nsCOMPtr newParentNode; + nsCOMPtr blockParentNode = do_QueryInterface(blockParentElement); + // we need to treat nodes directly inside the body differently + nsAutoString parentTag; + blockParentElement->GetTagName(parentTag); + PRBool isRoot; + IsRootTag(parentTag, isRoot); + if (PR_TRUE==isRoot) + { + // if nodeToReParent is a text node, we have Text. + // re-parent Text into a new at the offset of Text in + // so we end up with Text + // ignore aTransformation, replaces act like inserts + nsCOMPtr nodeAsText = do_QueryInterface(nodeToReParent); + if (nodeAsText) + { + res = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, + aTransformation, getter_AddRefs(newParentNode)); + } + else + { // this is the case of an insertion point between 2 non-text objects + // XXX: how to you know it's an insertion point??? + PRInt32 offsetInParent=0; + res = GetChildOffset(nodeToReParent, blockParentNode, offsetInParent); + NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); + // otherwise, just create the block parent at the selection + res = CreateNode(aParentTag, blockParentNode, offsetInParent, + getter_AddRefs(newParentNode)); + // XXX: need to move some of the children of blockParentNode into the newParentNode? + // XXX: need to create a bogus text node inside this new block? + // that means, I need to generalize bogus node handling + } + } + else + { // the block parent is not a ROOT, + // for the selected block content, transform blockParentNode + if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) || + (eInsertParent==aTransformation)) + { + if (gNoisy) { DebugDumpContent(); } // DEBUG + res = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, + aTransformation, getter_AddRefs(newParentNode)); + if ((NS_SUCCEEDED(res)) && (newParentNode) && (eReplaceParent==aTransformation)) + { + PRBool hasChildren; + blockParentNode->HasChildNodes(&hasChildren); + if (PR_FALSE==hasChildren) + { + res = nsEditor::DeleteNode(blockParentNode); + if (gNoisy) + { + printf("deleted old block parent node %p\n", blockParentNode.get()); + DebugDumpContent(); // DEBUG + } + } + } + } + else { // otherwise, it's a no-op + if (gNoisy) { printf("AddBlockParent is a no-op for this collapsed selection.\n"); } + } + } + } + return res; +} + + +NS_IMETHODIMP +nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, + nsString &aParentTag, + nsIDOMNode *aBlockParentNode, + nsString &aBlockParentTag, + BlockTransformationType aTransformation, + nsIDOMNode **aNewParentNode) +{ + if (!aNode || !aBlockParentNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; } + nsCOMPtr blockParentNode = do_QueryInterface(aBlockParentNode); + PRBool removeBlockParent = PR_FALSE; + PRBool removeBreakBefore = PR_FALSE; + PRBool removeBreakAfter = PR_FALSE; + nsCOMPtrancestor; + nsresult res = aNode->GetParentNode(getter_AddRefs(ancestor)); + nsCOMPtrpreviousAncestor = do_QueryInterface(aNode); + while (NS_SUCCEEDED(res) && ancestor) + { + nsCOMPtrancestorElement = do_QueryInterface(ancestor); + nsAutoString ancestorTag; + ancestorElement->GetTagName(ancestorTag); + if (ancestorTag.EqualsIgnoreCase(aBlockParentTag)) + { + break; // previousAncestor will contain the node to operate on + } + previousAncestor = do_QueryInterface(ancestor); + res = ancestorElement->GetParentNode(getter_AddRefs(ancestor)); + } + // now, previousAncestor is the node we are operating on + nsCOMPtrleftNode, rightNode; + res = GetBlockSection(previousAncestor, + getter_AddRefs(leftNode), + getter_AddRefs(rightNode)); + if ((NS_SUCCEEDED(res)) && leftNode && rightNode) + { + // determine some state for managing
s around the new block + PRBool isSubordinateBlock = PR_FALSE; // if true, the content is already in a subordinate block + PRBool isRootBlock = PR_FALSE; // if true, the content is in a root block + nsCOMPtrblockParentElement = do_QueryInterface(blockParentNode); + if (blockParentElement) + { + nsAutoString blockParentTag; + blockParentElement->GetTagName(blockParentTag); + IsSubordinateBlock(blockParentTag, isSubordinateBlock); + IsRootTag(blockParentTag, isRootBlock); + } + + if (PR_TRUE==isRootBlock) + { // we're creating a block element where a block element did not previously exist + removeBreakBefore = PR_TRUE; + removeBreakAfter = PR_TRUE; + } + + // apply the transformation + PRInt32 offsetInParent=0; + if (eInsertParent==aTransformation || PR_TRUE==isRootBlock) + { + res = GetChildOffset(leftNode, blockParentNode, offsetInParent); + NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); + res = CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); + if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } + } + else + { + nsCOMPtr grandParent; + res = blockParentNode->GetParentNode(getter_AddRefs(grandParent)); + if ((NS_SUCCEEDED(res)) && grandParent) + { + nsCOMPtrfirstChildNode, lastChildNode; + blockParentNode->GetFirstChild(getter_AddRefs(firstChildNode)); + blockParentNode->GetLastChild(getter_AddRefs(lastChildNode)); + if (firstChildNode==leftNode && lastChildNode==rightNode) + { + res = GetChildOffset(blockParentNode, grandParent, offsetInParent); + NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); + res = CreateNode(aParentTag, grandParent, offsetInParent, aNewParentNode); + if (gNoisy) { printf("created a node in grandParent at offset %d\n", offsetInParent); } + } + else + { + // We're in the case where the content of blockParentNode is separated by
's, + // creating multiple block content ranges. + // Split blockParentNode around the blockContent + if (gNoisy) { printf("splitting a node because of
s\n"); } + nsCOMPtr newLeftNode; + if (firstChildNode!=leftNode) + { + res = GetChildOffset(leftNode, blockParentNode, offsetInParent); + if (gNoisy) { printf("splitting left at %d\n", offsetInParent); } + res = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); + // after this split, blockParentNode still contains leftNode and rightNode + } + if (lastChildNode!=rightNode) + { + res = GetChildOffset(rightNode, blockParentNode, offsetInParent); + offsetInParent++; + if (gNoisy) { printf("splitting right at %d\n", offsetInParent); } + res = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); + blockParentNode = do_QueryInterface(newLeftNode); + } + res = GetChildOffset(leftNode, blockParentNode, offsetInParent); + NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); + res = CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); + if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } + // what we need to do here is remove the existing block parent when we're all done. + removeBlockParent = PR_TRUE; + } + } + } + if ((NS_SUCCEEDED(res)) && *aNewParentNode) + { // move all the children/contents of blockParentNode to aNewParentNode + nsCOMPtrchildNode = do_QueryInterface(rightNode); + nsCOMPtrpreviousSiblingNode; + while (NS_SUCCEEDED(res) && childNode) + { + childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); + // explicitly delete of childNode from it's current parent + // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! + res = nsEditor::DeleteNode(childNode); + if (NS_SUCCEEDED(res)) + { + res = nsEditor::InsertNode(childNode, *aNewParentNode, 0); + if (gNoisy) + { + printf("re-parented sibling node %p\n", childNode.get()); + } + } + if (childNode==leftNode || rightNode==leftNode) { + break; + } + childNode = do_QueryInterface(previousSiblingNode); + } // end while loop + } + // clean up the surrounding content to maintain vertical whitespace + if (NS_SUCCEEDED(res)) + { + // if the prior node is a
and we did something to change vertical whitespacing, delete the
+ nsCOMPtr brNode; + res = GetPriorNode(leftNode, PR_TRUE, getter_AddRefs(brNode)); + if (NS_SUCCEEDED(res) && brNode) + { + nsCOMPtr brContent = do_QueryInterface(brNode); + if (brContent) + { + nsCOMPtr brContentTag; + brContent->GetTag(*getter_AddRefs(brContentTag)); + if (nsIEditProperty::br==brContentTag.get()) { + res = DeleteNode(brNode); + } + } + } + + // if the next node is a
and we did something to change vertical whitespacing, delete the
+ if (NS_SUCCEEDED(res)) + { + res = GetNextNode(rightNode, PR_TRUE, getter_AddRefs(brNode)); + if (NS_SUCCEEDED(res) && brNode) + { + nsCOMPtr brContent = do_QueryInterface(brNode); + if (brContent) + { + nsCOMPtr brContentTag; + brContent->GetTag(*getter_AddRefs(brContentTag)); + if (nsIEditProperty::br==brContentTag.get()) { + res = DeleteNode(brNode); + } + } + } + } + } + if ((NS_SUCCEEDED(res)) && (PR_TRUE==removeBlockParent)) + { // we determined we need to remove the previous block parent. Do it! + // go through list backwards so deletes don't interfere with the iteration + nsCOMPtr childNodes; + res = blockParentNode->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(res)) && (childNodes)) + { + nsCOMPtrgrandParent; + blockParentNode->GetParentNode(getter_AddRefs(grandParent)); + //PRInt32 offsetInParent; + res = GetChildOffset(blockParentNode, grandParent, offsetInParent); + PRUint32 childCount; + childNodes->GetLength(&childCount); + PRInt32 i=childCount-1; + for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) + { + nsCOMPtr childNode; + res = childNodes->Item(i, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = DeleteNode(childNode); + if (NS_SUCCEEDED(res)) { + res = InsertNode(childNode, grandParent, offsetInParent); + } + } + } + if (gNoisy) { printf("removing the old block parent %p\n", blockParentNode.get()); } + res = DeleteNode(blockParentNode); + } + } + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, + nsString &aParentTag, + BlockTransformationType aTranformation) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsISupportsArray *blockSections; + res = NS_NewISupportsArray(&blockSections); + if ((NS_SUCCEEDED(res)) && blockSections) + { + res = GetBlockSectionsForRange(aRange, blockSections); + if (NS_SUCCEEDED(res)) + { + nsIDOMRange *subRange; + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + while (subRange && (NS_SUCCEEDED(res))) + { + nsCOMPtrstartParent; + res = subRange->GetStartParent(getter_AddRefs(startParent)); + if (NS_SUCCEEDED(res) && startParent) + { + if (gNoisy) { printf("ReParentContentOfRange calling ReParentContentOfNode\n"); } + res = ReParentContentOfNode(startParent, aParentTag, aTranformation); + } + NS_RELEASE(subRange); + blockSections->RemoveElementAt(0); + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + } + } + NS_RELEASE(blockSections); + } + return res; +} + + +NS_IMETHODIMP +nsHTMLEditor::RemoveParagraphStyleFromRange(nsIDOMRange *aRange) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsISupportsArray *blockSections; + res = NS_NewISupportsArray(&blockSections); + if ((NS_SUCCEEDED(res)) && blockSections) + { + res = GetBlockSectionsForRange(aRange, blockSections); + if (NS_SUCCEEDED(res)) + { + nsIDOMRange *subRange; + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + while (subRange && (NS_SUCCEEDED(res))) + { + res = RemoveParagraphStyleFromBlockContent(subRange); + NS_RELEASE(subRange); + blockSections->RemoveElementAt(0); + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + } + } + NS_RELEASE(blockSections); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsCOMPtrstartParent; + aRange->GetStartParent(getter_AddRefs(startParent)); + nsCOMPtrblockParentElement; + res = GetBlockParent(startParent, getter_AddRefs(blockParentElement)); + while ((NS_SUCCEEDED(res)) && blockParentElement) + { + nsAutoString blockParentTag; + blockParentElement->GetTagName(blockParentTag); + PRBool isSubordinateBlock; + IsSubordinateBlock(blockParentTag, isSubordinateBlock); + if (PR_FALSE==isSubordinateBlock) { + break; + } + else + { + // go through list backwards so deletes don't interfere with the iteration + nsCOMPtr childNodes; + res = blockParentElement->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(res)) && (childNodes)) + { + nsCOMPtrgrandParent; + blockParentElement->GetParentNode(getter_AddRefs(grandParent)); + PRInt32 offsetInParent; + res = GetChildOffset(blockParentElement, grandParent, offsetInParent); + PRUint32 childCount; + childNodes->GetLength(&childCount); + PRInt32 i=childCount-1; + for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) + { + nsCOMPtr childNode; + res = childNodes->Item(i, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = DeleteNode(childNode); + if (NS_SUCCEEDED(res)) { + res = InsertNode(childNode, grandParent, offsetInParent); + } + } + } + if (NS_SUCCEEDED(res)) { + res = DeleteNode(blockParentElement); + } + } + } + res = GetBlockParent(startParent, getter_AddRefs(blockParentElement)); + } + return res; +} + + +NS_IMETHODIMP +nsHTMLEditor::RemoveParentFromRange(const nsString &aParentTag, nsIDOMRange *aRange) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsISupportsArray *blockSections; + res = NS_NewISupportsArray(&blockSections); + if ((NS_SUCCEEDED(res)) && blockSections) + { + res = GetBlockSectionsForRange(aRange, blockSections); + if (NS_SUCCEEDED(res)) + { + nsIDOMRange *subRange; + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + while (subRange && (NS_SUCCEEDED(res))) + { + res = RemoveParentFromBlockContent(aParentTag, subRange); + NS_RELEASE(subRange); + blockSections->RemoveElementAt(0); + subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); + } + } + NS_RELEASE(blockSections); + } + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRange *aRange) +{ + if (!aRange) { return NS_ERROR_NULL_POINTER; } + nsresult res; + nsCOMPtrstartParent; + res = aRange->GetStartParent(getter_AddRefs(startParent)); + if ((NS_SUCCEEDED(res)) && startParent) + { + nsCOMPtrparentNode; + nsCOMPtrparentElement; + res = startParent->GetParentNode(getter_AddRefs(parentNode)); + while ((NS_SUCCEEDED(res)) && parentNode) + { + parentElement = do_QueryInterface(parentNode); + nsAutoString parentTag; + parentElement->GetTagName(parentTag); + PRBool isRoot; + IsRootTag(parentTag, isRoot); + if (aParentTag.EqualsIgnoreCase(parentTag)) + { + // go through list backwards so deletes don't interfere with the iteration + nsCOMPtr childNodes; + res = parentElement->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(res)) && (childNodes)) + { + nsCOMPtrgrandParent; + parentElement->GetParentNode(getter_AddRefs(grandParent)); + PRInt32 offsetInParent; + res = GetChildOffset(parentElement, grandParent, offsetInParent); + PRUint32 childCount; + childNodes->GetLength(&childCount); + PRInt32 i=childCount-1; + for ( ; ((NS_SUCCEEDED(res)) && (0<=i)); i--) + { + nsCOMPtr childNode; + res = childNodes->Item(i, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = DeleteNode(childNode); + if (NS_SUCCEEDED(res)) { + res = InsertNode(childNode, grandParent, offsetInParent); + } + } + } + if (NS_SUCCEEDED(res)) { + res = DeleteNode(parentElement); + } + } + break; + } + else if (PR_TRUE==isRoot) { // hit a local root node, terminate loop + break; + } + res = parentElement->GetParentNode(getter_AddRefs(parentNode)); + } + } + return res; +} + + + +PRBool nsHTMLEditor::IsElementInBody(nsIDOMElement* aElement) +{ + if ( aElement ) + { + nsIDOMElement* bodyElement = nsnull; + nsresult res = nsEditor::GetBodyElement(&bodyElement); + if (NS_SUCCEEDED(res) && bodyElement) + { + nsCOMPtr parent; + nsCOMPtr currentElement = do_QueryInterface(aElement); + if (currentElement) + { + do { + currentElement->GetParentNode(getter_AddRefs(parent)); + if (parent) + { + if (parent == bodyElement) + return PR_TRUE; + + currentElement = parent; + } + } while(parent); + } + } + } + return PR_FALSE; +} + +PRBool +nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement) +{ + PRBool caretIsSet = PR_FALSE; + + if (aElement && IsElementInBody(aElement)) + { + nsresult res = NS_OK; + nsAutoString tagName; + aElement->GetNodeName(tagName); + tagName.ToLowerCase(); + if (tagName == "table" || tagName == "tr" || + tagName == "td" || tagName == "th" || + tagName == "thead" || tagName == "tfoot" || + tagName == "tbody" || tagName == "caption") + { + nsCOMPtr node = do_QueryInterface(aElement); + nsCOMPtr parent; + // This MUST succeed if IsElementInBody was TRUE + node->GetParentNode(getter_AddRefs(parent)); + nsCOMPtrfirstChild; + // Find deepest child + PRBool hasChild; + while (NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild) + { + if (NS_SUCCEEDED(node->GetFirstChild(getter_AddRefs(firstChild)))) + { + parent = node; + node = firstChild; + } + } + PRInt32 offset = 0; + nsCOMPtrlastChild; + res = parent->GetLastChild(getter_AddRefs(lastChild)); + if (NS_SUCCEEDED(res) && lastChild && node != lastChild) + { + if (node == lastChild) + { + // Check if node is text and has more than just a   + nsCOMPtrtextNode = do_QueryInterface(node); + nsString text; + char nbspStr[2] = {nbsp, 0}; + if (textNode && textNode->GetData(text)) + { + // Set selection relative to the text node + parent = node; + PRInt32 len = text.Length(); + if (len > 1 || text != nbspStr) + { + offset = len; + } + } + } else { + // We have > 1 node, so set to end of content + } + } + // Set selection at beginning of deepest node + // Should we set + nsCOMPtr selection; + res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(res) && selection) + { + res = selection->Collapse(parent, offset); + if (NS_SUCCEEDED(res)) + caretIsSet = PR_TRUE; + } + } + } + return caretIsSet; +} + + + NS_IMETHODIMP nsHTMLEditor::IsRootTag(nsString &aTag, PRBool &aIsTag) { @@ -2758,54 +4341,1342 @@ nsHTMLEditor::IsSubordinateBlock(nsString &aTag, PRBool &aIsTag) return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::BeginComposition(void) -{ - return nsTextEditor::BeginComposition(); -} - -NS_IMETHODIMP nsHTMLEditor::EndComposition(void) -{ - return nsTextEditor::EndComposition(); -} - -NS_IMETHODIMP nsHTMLEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList, nsTextEventReply* aReply) -{ - return nsTextEditor::SetCompositionString(aCompositionString,aTextRangeList,aReply); -} NS_IMETHODIMP -nsHTMLEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) +nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode) { -#ifdef DEBUG - if (!outNumTests || !outNumTestsFailed) - return NS_ERROR_NULL_POINTER; - - // first, run the text editor tests (is this appropriate?) - nsresult rv = nsTextEditor::DebugUnitTests(outNumTests, outNumTestsFailed); - if (NS_FAILED(rv)) - return rv; - - // now run our tests - - - - *outNumTests += 0; - *outNumTestsFailed += 0; - return NS_OK; -#else - return NS_ERROR_NOT_IMPLEMENTED; + nsresult result=NS_ERROR_NOT_INITIALIZED; + nsCOMPtr selection; + result = GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + PRBool collapsed; + result = selection->GetIsCollapsed(&collapsed); + if (NS_SUCCEEDED(result) && !collapsed) + { + result = DeleteSelection(nsIEditor::eDoNothing); + if (NS_FAILED(result)) { + return result; + } + // get the new selection + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) { + return result; + } +#ifdef NS_DEBUG + nsCOMPtrtestSelectedNode; + nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode)); + // no selection is ok. + // if there is a selection, it must be collapsed + if (testSelectedNode) + { + PRBool testCollapsed; + debugResult = selection->GetIsCollapsed(&testCollapsed); + NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); + NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion"); + } #endif + } + // split the selected node + PRInt32 offsetOfSelectedNode; + result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); + if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode) + { + nsCOMPtr selectedNode; + PRUint32 selectedNodeContentCount=0; + nsCOMPtrselectedParentNodeAsText; + selectedParentNodeAsText = do_QueryInterface(parentSelectedNode); + + /* if the selection is a text node, split the text node if necesary + and compute where to put the new node + */ + if (selectedParentNodeAsText) + { + PRInt32 indexOfTextNodeInParent; + selectedNode = do_QueryInterface(parentSelectedNode); + selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode)); + selectedParentNodeAsText->GetLength(&selectedNodeContentCount); + GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent); + + if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) + { + nsCOMPtr newSiblingNode; + result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); + // now get the node's offset in it's parent, and insert the new tag there + if (NS_SUCCEEDED(result)) { + result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + } + } + else + { // determine where to insert the new node + if (0==offsetOfSelectedNode) { + offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent + } + else { // insert new node as last child + GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node + } + } + } + /* if the selection is not a text node, split the parent node if necesary + and compute where to put the new node + */ + else + { // it's an interior node + nsCOMPtrparentChildList; + parentSelectedNode->GetChildNodes(getter_AddRefs(parentChildList)); + if ((NS_SUCCEEDED(result)) && parentChildList) + { + result = parentChildList->Item(offsetOfSelectedNode, getter_AddRefs(selectedNode)); + if ((NS_SUCCEEDED(result)) && selectedNode) + { + nsCOMPtrselectedNodeAsText; + selectedNodeAsText = do_QueryInterface(selectedNode); + nsCOMPtrchildList; + //CM: I added "result =" + result = selectedNode->GetChildNodes(getter_AddRefs(childList)); + if (NS_SUCCEEDED(result)) + { + if (childList) + { + childList->GetLength(&selectedNodeContentCount); + } + else + { + // This is the case where the collapsed selection offset + // points to an inline node with no children + // This must also be where the new node should be inserted + // and there is no splitting necessary + offsetOfNewNode = offsetOfSelectedNode; + return NS_OK; + } + } + else + { + return NS_ERROR_FAILURE; + } + if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) + { + nsCOMPtr newSiblingNode; + result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); + // now get the node's offset in it's parent, and insert the new tag there + if (NS_SUCCEEDED(result)) { + result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + } + } + else + { // determine where to insert the new node + if (0==offsetOfSelectedNode) { + offsetOfNewNode = 0; // insert new node as first child + } + else { // insert new node as last child + GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); + offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node + } + } + } + } + } + + // Here's where the new node was inserted + } + else { + printf("InsertBreak into an empty document is not yet supported\n"); + } + } + return result; +} + + +#pragma mark - + +nsCOMPtr nsHTMLEditor::FindPreElement() +{ + nsCOMPtr domdoc; + nsEditor::GetDocument(getter_AddRefs(domdoc)); + if (!domdoc) + return 0; + + nsCOMPtr doc (do_QueryInterface(domdoc)); + if (!doc) + return 0; + + nsIContent* rootContent = doc->GetRootContent(); + if (!rootContent) + return 0; + + nsCOMPtr rootNode (do_QueryInterface(rootContent)); + if (!rootNode) + return 0; + + nsString prestr ("PRE"); // GetFirstNodeOfType requires capitals + nsCOMPtr preNode; + if (!NS_SUCCEEDED(nsEditor::GetFirstNodeOfType(rootNode, prestr, + getter_AddRefs(preNode)))) + return 0; + + return do_QueryInterface(preNode); +} + + +void nsHTMLEditor::HandleEventListenerError() +{ + printf("failed to add event listener\n"); + // null out the nsCOMPtrs + mKeyListenerP = nsnull; + mMouseListenerP = nsnull; + mTextListenerP = nsnull; + mDragListenerP = nsnull; + mCompositionListenerP = nsnull; + mFocusListenerP = nsnull; +} + + +TypeInState * nsHTMLEditor::GetTypeInState() +{ + if (mTypeInState) { + NS_ADDREF(mTypeInState); + } + return mTypeInState; +} + + +NS_IMETHODIMP +nsHTMLEditor::SetTextPropertiesForNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue) +{ + if (gNoisy) { printf("nsTextEditor::SetTextPropertyForNode\n"); } + nsresult result=NS_OK; + PRBool textPropertySet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(aNode, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); + if (PR_FALSE==textPropertySet) + { + if (aValue && 0!=aValue->Length()) + { + result = RemoveTextPropertiesForNode(aNode, aParent, aStartOffset, aEndOffset, aPropName, aAttribute); + } + nsAutoString tag; + aPropName->ToString(tag); + if (NS_SUCCEEDED(result)) + { + nsCOMPtrnewStyleNode; + result = nsEditor::CreateNode(tag, aParent, 0, getter_AddRefs(newStyleNode)); + if (NS_SUCCEEDED(result) && newStyleNode) + { + nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(aNode); + if (nodeAsChar) + { + result = MoveContentOfNodeIntoNewParent(aNode, newStyleNode, aStartOffset, aEndOffset); + } + else + { // handle non-text selection + nsCOMPtr parent; // used just to make the code easier to understand + nsCOMPtr child; + parent = do_QueryInterface(aNode); + child = GetChildAt(parent, aStartOffset); + // XXX: need to loop for aStartOffset!=aEndOffset-1? + PRInt32 offsetInParent = aStartOffset; // remember where aNode was in aParent + if (NS_SUCCEEDED(result)) + { // remove child from parent + result = nsEditor::DeleteNode(child); + if (NS_SUCCEEDED(result)) + { // put child into the newStyleNode + result = nsEditor::InsertNode(child, newStyleNode, 0); + if (NS_SUCCEEDED(result)) + { // put newStyleNode in parent where child was + result = nsEditor::InsertNode(newStyleNode, parent, offsetInParent); + } + } + } + } + if (NS_SUCCEEDED(result)) + { + if (aAttribute && 0!=aAttribute->Length()) + { + nsCOMPtr newStyleElement; + newStyleElement = do_QueryInterface(newStyleNode); + nsAutoString value; + if (aValue) { + value = *aValue; + } + // XXX should be a call to editor to change attribute! + result = newStyleElement->SetAttribute(*aAttribute, value); + } + } + } + } + } + if (gNoisy) { printf("SetTextPropertyForNode returning %d, dumping content...\n", result);} + if (gNoisy) {DebugDumpContent(); } // DEBUG + return result; +} + +NS_IMETHODIMP nsHTMLEditor::MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, + nsIDOMNode *aNewParentNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset) +{ + if (!aNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; } + if (gNoisy) { printf("nsTextEditor::MoveContentOfNodeIntoNewParent\n"); } + nsresult result=NS_OK; + + PRUint32 count; + result = GetLengthOfDOMNode(aNode, count); + + if (NS_SUCCEEDED(result)) + { + nsCOMPtrnewChildNode; // this will be the child node we move into the new style node + // split the node at the start offset unless the split would create an empty node + if (aStartOffset!=0) + { + result = nsEditor::SplitNode(aNode, aStartOffset, getter_AddRefs(newChildNode)); + if (gNoisy) { printf("* split created left node %p\n", newChildNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + if (NS_SUCCEEDED(result)) + { + if (aEndOffset!=(PRInt32)count) + { + result = nsEditor::SplitNode(aNode, aEndOffset-aStartOffset, getter_AddRefs(newChildNode)); + if (gNoisy) { printf("* split created left node %p\n", newChildNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + else + { + newChildNode = do_QueryInterface(aNode); + if (gNoisy) { printf("* second split not required, new text node set to aNode = %p\n", newChildNode.get());} + } + if (NS_SUCCEEDED(result)) + { + // move aNewParentNode into the right location + + // optimization: if all we're doing is changing a value for an existing attribute for the + // entire selection, then just twiddle the existing style node + PRBool done = PR_FALSE; // set to true in optimized case if we can really do the optimization + /* + if (aAttribute && aValue && (0==aStartOffset) && (aEndOffset==(PRInt32)count)) + { + // ??? can we really compute this? + } + */ + if (PR_FALSE==done) + { + // if we've ended up with an empty text node, just delete it and we're done + nsCOMPtrnewChildNodeAsChar; + newChildNodeAsChar = do_QueryInterface(newChildNode); + PRUint32 newChildNodeLength; + if (newChildNodeAsChar) + { + newChildNodeAsChar->GetLength(&newChildNodeLength); + if (0==newChildNodeLength) + { + result = nsEditor::DeleteNode(newChildNode); + done = PR_TRUE; + // XXX: need to set selection here + } + } + // move the new child node into the new parent + if (PR_FALSE==done) + { + // first, move the new parent into the correct location + PRInt32 offsetInParent; + nsCOMPtrparentNode; + result = aNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::DeleteNode(aNewParentNode); + if (NS_SUCCEEDED(result)) + { // must get child offset AFTER delete of aNewParentNode! + result = GetChildOffset(aNode, parentNode, offsetInParent); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::InsertNode(aNewParentNode, parentNode, offsetInParent); + if (NS_SUCCEEDED(result)) + { + // then move the new child into the new parent node + result = nsEditor::DeleteNode(newChildNode); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::InsertNode(newChildNode, aNewParentNode, 0); + if (NS_SUCCEEDED(result)) + { // set the selection + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result)) + { + selection->Collapse(newChildNode, 0); + PRInt32 endOffset = aEndOffset-aStartOffset; + selection->Extend(newChildNode, endOffset); + } + } + } + } + } + } + } + } + } + } + } + } + return result; +} + +/* this should only get called if the only intervening nodes are inline style nodes */ +NS_IMETHODIMP +nsHTMLEditor::SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue) +{ + if (gNoisy) { printf("nsTextEditor::SetTextPropertiesForNodesWithSameParent\n"); } + nsresult result=NS_OK; + PRBool textPropertySet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(aStartNode, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); + if (PR_FALSE==textPropertySet) + { + if (aValue && 0!=aValue->Length()) + { + result = RemoveTextPropertiesForNodesWithSameParent(aStartNode, aStartOffset, + aEndNode, aEndOffset, + aParent, aPropName, aAttribute); + } + nsAutoString tag; + aPropName->ToString(tag); + // create the new style node, which will be the new parent for the selected nodes + nsCOMPtrnewStyleNode; + nsCOMPtrdoc; + result = GetDocument(getter_AddRefs(doc)); + if (NS_SUCCEEDED(result) && doc) + { + nsCOMPtrnewElement; + result = doc->CreateElement(tag, getter_AddRefs(newElement)); + if (NS_SUCCEEDED(result) && newElement) + { + newStyleNode = do_QueryInterface(newElement); + } + } + if (NS_SUCCEEDED(result) && newStyleNode) + { + result = MoveContiguousContentIntoNewParent(aStartNode, aStartOffset, aEndNode, aEndOffset, aParent, newStyleNode); + if (NS_SUCCEEDED(result) && aAttribute && 0!=aAttribute->Length()) + { + nsCOMPtr newStyleElement; + newStyleElement = do_QueryInterface(newStyleNode); + nsAutoString value; + if (aValue) { + value = *aValue; + } + result = newStyleElement->SetAttribute(*aAttribute, value); + } + } + } + return result; +} + +//XXX won't work for selections that are not leaf nodes! +// should fix up the end points to make sure they are leaf nodes +NS_IMETHODIMP +nsHTMLEditor::MoveContiguousContentIntoNewParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aGrandParentNode, + nsIDOMNode *aNewParentNode) +{ + if (!aStartNode || !aEndNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; } + if (gNoisy) { printf("nsTextEditor::MoveContiguousContentIntoNewParent\n"); } + + nsresult result = NS_OK; + nsCOMPtrstartNode, endNode; + PRInt32 startOffset = aStartOffset; // this will be the left edge of what we change + PRInt32 endOffset = aEndOffset; // this will be the right edge of what we change + nsCOMPtrnewLeftNode; // this will be the middle text node + if (IsTextNode(aStartNode)) + { + startOffset = 0; + if (gNoisy) { printf("aStartNode is a text node.\n"); } + startNode = do_QueryInterface(aStartNode); + if (0!=aStartOffset) + { + result = nsEditor::SplitNode(aStartNode, aStartOffset, getter_AddRefs(newLeftNode)); + if (gNoisy) { printf("split aStartNode, newLeftNode = %p\n", newLeftNode.get()); } + } + else { + if (gNoisy) { printf("did not split aStartNode\n"); } + } + } + else { + startNode = GetChildAt(aStartNode, aStartOffset); + if (gNoisy) { printf("aStartNode is not a text node, got startNode = %p.\n", startNode.get()); } + } + if (NS_SUCCEEDED(result)) + { + nsCOMPtrnewRightNode; // this will be the middle text node + if (IsTextNode(aEndNode)) + { + if (gNoisy) { printf("aEndNode is a text node.\n"); } + endNode = do_QueryInterface(aEndNode); + PRUint32 count; + GetLengthOfDOMNode(aEndNode, count); + if ((PRInt32)count!=aEndOffset) + { + result = nsEditor::SplitNode(aEndNode, aEndOffset, getter_AddRefs(newRightNode)); + if (gNoisy) { printf("split aEndNode, newRightNode = %p\n", newRightNode.get()); } + } + else { + newRightNode = do_QueryInterface(aEndNode); + if (gNoisy) { printf("did not split aEndNode\n"); } + } + } + else + { + endNode = GetChildAt(aEndNode, aEndOffset-1); + newRightNode = do_QueryInterface(endNode); + if (gNoisy) { printf("aEndNode is not a text node, got endNode = %p.\n", endNode.get()); } + } + if (NS_SUCCEEDED(result)) + { + PRInt32 offsetInParent; + result = GetChildOffset(startNode, aGrandParentNode, offsetInParent); + /* + if (newLeftNode) { + result = GetChildOffset(newLeftNode, aGrandParentNode, offsetInParent); + } + else { + offsetInParent = 0; // relies on +1 below in call to CreateNode + } + */ + if (NS_SUCCEEDED(result)) + { + // insert aNewParentNode into aGrandParentNode + result = nsEditor::InsertNode(aNewParentNode, aGrandParentNode, offsetInParent); + if (NS_SUCCEEDED(result)) + { // move the right half of the start node into the new parent node + nsCOMPtrintermediateNode; + result = startNode->GetNextSibling(getter_AddRefs(intermediateNode)); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::DeleteNode(startNode); + if (NS_SUCCEEDED(result)) + { + PRInt32 childIndex=0; + result = nsEditor::InsertNode(startNode, aNewParentNode, childIndex); + childIndex++; + if (NS_SUCCEEDED(result)) + { // move all the intermediate nodes into the new parent node + nsCOMPtrnextSibling; + while (intermediateNode.get() != endNode.get()) + { + if (!intermediateNode) + result = NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) { + break; + } + // get the next sibling before moving the current child!!! + intermediateNode->GetNextSibling(getter_AddRefs(nextSibling)); + result = nsEditor::DeleteNode(intermediateNode); + if (NS_SUCCEEDED(result)) { + result = nsEditor::InsertNode(intermediateNode, aNewParentNode, childIndex); + childIndex++; + } + intermediateNode = do_QueryInterface(nextSibling); + } + if (NS_SUCCEEDED(result)) + { // move the left half of the end node into the new parent node + result = nsEditor::DeleteNode(newRightNode); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::InsertNode(newRightNode, aNewParentNode, childIndex); + // now set the selection + if (NS_SUCCEEDED(result)) + { + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result)) + { + selection->Collapse(startNode, startOffset); + selection->Extend(newRightNode, endOffset); + } + } + } + } + } + } + } + } + } + } + } + return result; +} + + +/* this wraps every selected text node in a new inline style node if needed + the text nodes are treated as being unique -- each needs it's own style node + if the style is not already present. + each action has immediate effect on the content tree and resolved style, so + doing outermost text nodes first removes the need for interior style nodes in some cases. + XXX: need to code test to see if new style node is needed +*/ +NS_IMETHODIMP +nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, + nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue) +{ + if (gNoisy) { printf("start nsTextEditor::SetTextPropertiesForNodeWithDifferentParents\n"); } + nsresult result=NS_OK; + PRUint32 count; + if (!aRange || !aStartNode || !aEndNode || !aParent || !aPropName) + return NS_ERROR_NULL_POINTER; + + PRInt32 startOffset, endOffset; + // create a style node for the text in the start parent + nsCOMPtrparent; + + result = RemoveTextPropertiesForNodeWithDifferentParents(aStartNode,aStartOffset, + aEndNode, aEndOffset, + aParent, aPropName, aAttribute); + if (NS_FAILED(result)) { return result; } + + // RemoveTextProperties... might have changed selection endpoints, get new ones + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) { return result; } + if (!selection) { return NS_ERROR_NULL_POINTER; } + selection->GetAnchorOffset(&aStartOffset); + selection->GetFocusOffset(&aEndOffset); + + // create new parent nodes for all the content between the start and end nodes + nsCOMPtriter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + // find our starting point + PRBool startIsText = IsTextNode(aStartNode); + nsCOMPtrstartContent; + if (PR_TRUE==startIsText) { + startContent = do_QueryInterface(aStartNode); + } + else { + nsCOMPtrnode = GetChildAt(aStartNode, aStartOffset); + startContent = do_QueryInterface(node); + } + + // find our ending point + PRBool endIsText = IsTextNode(aEndNode); + nsCOMPtrendContent; + if (PR_TRUE==endIsText) { + endContent = do_QueryInterface(aEndNode); + } + else + { + nsCOMPtrtheEndNode; + if (aEndOffset>0) + { + theEndNode = GetChildAt(aEndNode, aEndOffset-1); + } + else { + // XXX: we need to find the previous node and set the selection correctly + NS_ASSERTION(0, "unexpected selection"); + return NS_ERROR_NOT_IMPLEMENTED; + } + endContent = do_QueryInterface(theEndNode); + } + + if (!startContent || !endContent) { return NS_ERROR_NULL_POINTER; } + // iterate over the nodes between the starting and ending points + iter->Init(aRange); + nsCOMPtr content; + iter->CurrentNode(getter_AddRefs(content)); + nsAutoString tag; + aPropName->ToString(tag); + while (NS_COMFALSE == iter->IsDone()) + { + if ((content.get() != startContent.get()) && + (content.get() != endContent.get())) + { + nsCOMPtrnode; + node = do_QueryInterface(content); + if (IsEditable(node)) + { + PRBool canContainChildren; + content->CanContainChildren(canContainChildren); + if (PR_FALSE==canContainChildren) + { + nsEditor::GetTagString(node,tag); + if (tag != "br") // skip
, even though it's a leaf + { // only want to wrap the text node in a new style node if it doesn't already have that style + if (gNoisy) { printf("node %p is an editable leaf.\n", node.get()); } + PRBool textPropertySet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(node, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); + if (PR_FALSE==textPropertySet) + { + if (gNoisy) { printf("property not set\n"); } + node->GetParentNode(getter_AddRefs(parent)); + if (!parent) { return NS_ERROR_NULL_POINTER; } + nsCOMPtrparentContent; + parentContent = do_QueryInterface(parent); + nsCOMPtrparentNode = do_QueryInterface(parent); + if (PR_TRUE==IsTextNode(node)) + { + startOffset = 0; + result = GetLengthOfDOMNode(node, (PRUint32&)endOffset); + } + else + { + parentContent->IndexOf(content, startOffset); + endOffset = startOffset+1; + } + if (gNoisy) { printf("start/end = %d %d\n", startOffset, endOffset); } + if (NS_SUCCEEDED(result)) { + result = SetTextPropertiesForNode(node, parentNode, startOffset, endOffset, aPropName, aAttribute, aValue); + } + } + } + } + } + // XXX: shouldn't there be an else here for non-text leaf nodes? + } + // note we don't check the result, we just rely on iter->IsDone + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + // handle endpoints + if (NS_SUCCEEDED(result)) + { + // create a style node for the text in the start parent + nsCOMPtrstartNode = do_QueryInterface(startContent); + result = startNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(result)) { + return result; + } + nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(startNode); + if (nodeAsChar) + { + nodeAsChar->GetLength(&count); + if (gNoisy) { printf("processing start node %p.\n", nodeAsChar.get()); } + result = SetTextPropertiesForNode(startNode, parent, aStartOffset, count, aPropName, aAttribute, aValue); + startOffset = 0; + } + else + { + nsCOMPtrgrandParent; + result = parent->GetParentNode(getter_AddRefs(grandParent)); + if (gNoisy) { printf("processing start node %p.\n", parent.get()); } + result = SetTextPropertiesForNode(parent, grandParent, aStartOffset, aStartOffset+1, aPropName, aAttribute, aValue); + startNode = do_QueryInterface(parent); + startOffset = aStartOffset; + } + + + if (NS_SUCCEEDED(result)) + { + // create a style node for the text in the end parent + nsCOMPtrendNode = do_QueryInterface(endContent); + result = endNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(result)) { + return result; + } + nodeAsChar = do_QueryInterface(endNode); + if (nodeAsChar) + { + nodeAsChar->GetLength(&count); + if (gNoisy) { printf("processing end node %p.\n", nodeAsChar.get()); } + result = SetTextPropertiesForNode(endNode, parent, 0, aEndOffset, aPropName, aAttribute, aValue); + // SetTextPropertiesForNode kindly computed the proper selection focus node and offset for us, + // remember them here + selection->GetFocusOffset(&endOffset); + selection->GetFocusNode(getter_AddRefs(endNode)); + } + else + { + NS_ASSERTION(0!=aEndOffset, "unexpected selection end offset"); + if (0==aEndOffset) { return NS_ERROR_NOT_IMPLEMENTED; } + nsCOMPtrgrandParent; + result = parent->GetParentNode(getter_AddRefs(grandParent)); + if (gNoisy) { printf("processing end node %p.\n", parent.get()); } + result = SetTextPropertiesForNode(parent, grandParent, aEndOffset-1, aEndOffset, aPropName, aAttribute, aValue); + endNode = do_QueryInterface(parent); + endOffset = 0; + } + if (NS_SUCCEEDED(result)) + { + + selection->Collapse(startNode, startOffset); + selection->Extend(endNode, aEndOffset); + } + } + } + } + if (gNoisy) { printf("end nsTextEditor::SetTextPropertiesForNodeWithDifferentParents\n"); } + + return result; } NS_IMETHODIMP -nsHTMLEditor::StartLogging(nsIFileSpec *aLogFile) +nsHTMLEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aPropName, + const nsString *aAttribute) { - return nsTextEditor::StartLogging(aLogFile); + if (gNoisy) { printf("nsTextEditor::RemoveTextPropertyForNode\n"); } + nsresult result=NS_OK; + nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(aNode); + PRBool textPropertySet; + nsCOMPtrresultNode; + IsTextPropertySetByContent(aNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); + if (PR_TRUE==textPropertySet) + { + nsCOMPtrparent; // initially set to first interior parent node to process + nsCOMPtrnewMiddleNode; // this will be the middle node after any required splits + nsCOMPtrnewLeftNode; // this will be the leftmost node, + // the node being split will be rightmost + PRUint32 count; + // if aNode is a text node, treat is specially + if (nodeAsChar) + { + nodeAsChar->GetLength(&count); + // split the node, and all parent nodes up to the style node + // then promote the selected content to the parent of the style node + if (0!=aStartOffset) { + if (gNoisy) { printf("* splitting text node %p at %d\n", aNode, aStartOffset);} + result = nsEditor::SplitNode(aNode, aStartOffset, getter_AddRefs(newLeftNode)); + if (gNoisy) { printf("* split created left node %p\n", newLeftNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + if (NS_SUCCEEDED(result)) + { + if ((PRInt32)count!=aEndOffset) { + if (gNoisy) { printf("* splitting text node (right node) %p at %d\n", aNode, aEndOffset-aStartOffset);} + result = nsEditor::SplitNode(aNode, aEndOffset-aStartOffset, getter_AddRefs(newMiddleNode)); + if (gNoisy) { printf("* split created middle node %p\n", newMiddleNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + else { + if (gNoisy) { printf("* no need to split text node, middle to aNode\n");} + newMiddleNode = do_QueryInterface(aNode); + } + NS_ASSERTION(newMiddleNode, "no middle node created"); + // now that the text node is split, split parent nodes until we get to the style node + parent = do_QueryInterface(aParent); // we know this has to succeed, no need to check + } + } + else { + newMiddleNode = do_QueryInterface(aNode); + parent = do_QueryInterface(aParent); + } + if (NS_SUCCEEDED(result) && newMiddleNode) + { + // split every ancestor until we find the node that is giving us the style we want to remove + // then split the style node and promote the selected content to the style node's parent + while (NS_SUCCEEDED(result) && parent) + { + if (gNoisy) { printf("* looking at parent %p\n", parent.get());} + // get the tag from parent and see if we're done + nsCOMPtrtemp; + nsCOMPtrelement; + element = do_QueryInterface(parent); + if (element) + { + nsAutoString tag; + result = element->GetTagName(tag); + if (gNoisy) { printf("* parent has tag %s\n", tag.ToNewCString()); } // XXX leak! + if (NS_SUCCEEDED(result)) + { + if (PR_FALSE==tag.EqualsIgnoreCase(aPropName->GetUnicode())) + { + PRInt32 offsetInParent; + result = GetChildOffset(newMiddleNode, parent, offsetInParent); + if (NS_SUCCEEDED(result)) + { + if (0!=offsetInParent) { + if (gNoisy) { printf("* splitting parent %p at offset %d\n", parent.get(), offsetInParent);} + result = nsEditor::SplitNode(parent, offsetInParent, getter_AddRefs(newLeftNode)); + if (gNoisy) { printf("* split created left node %p sibling of parent\n", newLeftNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + if (NS_SUCCEEDED(result)) + { + nsCOMPtrchildNodes; + result = parent->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_SUCCEEDED(result) && childNodes) + { + childNodes->GetLength(&count); + NS_ASSERTION(count>0, "bad child count in newly split node"); + if ((PRInt32)count!=1) + { + if (gNoisy) { printf("* splitting parent %p at offset %d\n", parent.get(), 1);} + result = nsEditor::SplitNode(parent, 1, getter_AddRefs(newMiddleNode)); + if (gNoisy) { printf("* split created middle node %p sibling of parent\n", newMiddleNode.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + else { + if (gNoisy) { printf("* no need to split parent, newMiddleNode=parent\n");} + newMiddleNode = do_QueryInterface(parent); + } + NS_ASSERTION(newMiddleNode, "no middle node created"); + parent->GetParentNode(getter_AddRefs(temp)); + parent = do_QueryInterface(temp); + } + } + } + } + // else we've found the style tag (referred to by "parent") + // newMiddleNode is the node that is an ancestor to the selection + else + { + if (gNoisy) { printf("* this is the style node\n");} + PRInt32 offsetInParent; + result = GetChildOffset(newMiddleNode, parent, offsetInParent); + if (NS_SUCCEEDED(result)) + { + nsCOMPtrchildNodes; + result = parent->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_SUCCEEDED(result) && childNodes) + { + childNodes->GetLength(&count); + // if there are siblings to the right, split parent at offsetInParent+1 + if ((PRInt32)count!=offsetInParent+1) + { + nsCOMPtrnewRightNode; + //nsCOMPtrtemp; + if (gNoisy) { printf("* splitting parent %p at offset %d for right side\n", parent.get(), offsetInParent+1);} + result = nsEditor::SplitNode(parent, offsetInParent+1, getter_AddRefs(temp)); + if (NS_SUCCEEDED(result)) + { + newRightNode = do_QueryInterface(parent); + parent = do_QueryInterface(temp); + if (gNoisy) { printf("* split created right node %p sibling of parent %p\n", newRightNode.get(), parent.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + } + if (NS_SUCCEEDED(result) && 0!=offsetInParent) { + if (gNoisy) { printf("* splitting parent %p at offset %d for left side\n", parent.get(), offsetInParent);} + result = nsEditor::SplitNode(parent, offsetInParent, getter_AddRefs(newLeftNode)); + if (gNoisy) { printf("* split created left node %p sibling of parent %p\n", newLeftNode.get(), parent.get());} + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + if (NS_SUCCEEDED(result)) + { // promote the selection to the grandparent + // first, determine the child's position in it's parent + PRInt32 childPositionInParent; + GetChildOffset(newMiddleNode, parent, childPositionInParent); + // compare childPositionInParent to the number of children in parent + //PRUint32 count=0; + //nsCOMPtrchildNodes; + result = parent->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_SUCCEEDED(result) && childNodes) { + childNodes->GetLength(&count); + } + PRBool insertAfter = PR_FALSE; + // if they're equal, we'll insert newMiddleNode in grandParent after the parent + if ((PRInt32)count==childPositionInParent) { + insertAfter = PR_TRUE; + } + // now that we know where to put newMiddleNode, do it. + nsCOMPtrgrandParent; + result = parent->GetParentNode(getter_AddRefs(grandParent)); + if (NS_SUCCEEDED(result) && grandParent) + { + if (gNoisy) { printf("* deleting middle node %p\n", newMiddleNode.get());} + result = nsEditor::DeleteNode(newMiddleNode); + if (gNoisy) {DebugDumpContent(); } // DEBUG + if (NS_SUCCEEDED(result)) + { + PRInt32 position; + result = GetChildOffset(parent, grandParent, position); + if (NS_SUCCEEDED(result)) + { + if (PR_TRUE==insertAfter) + { + if (gNoisy) {printf("insertAfter=PR_TRUE, incr. position\n"); } + position++; + } + if (gNoisy) { + printf("* inserting node %p in grandparent %p at offset %d\n", + newMiddleNode.get(), grandParent.get(), position); + } + result = nsEditor::InsertNode(newMiddleNode, grandParent, position); + if (gNoisy) {DebugDumpContent(); } // DEBUG + if (NS_SUCCEEDED(result)) + { + PRBool hasChildren=PR_TRUE; + parent->HasChildNodes(&hasChildren); + if (PR_FALSE==hasChildren) { + if (gNoisy) { printf("* deleting empty style node %p\n", parent.get());} + result = nsEditor::DeleteNode(parent); + if (gNoisy) {DebugDumpContent(); } // DEBUG + } + } + } + } + } + } + } + } + break; + } + } + } + } + } + } + return result; +} + +/* this should only get called if the only intervening nodes are inline style nodes */ +NS_IMETHODIMP +nsHTMLEditor::RemoveTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute) +{ + if (gNoisy) { printf("nsTextEditor::RemoveTextPropertiesForNodesWithSameParent\n"); } + nsresult result=NS_OK; + PRInt32 startOffset = aStartOffset; + PRInt32 endOffset; + nsCOMPtrnodeAsChar; + nsCOMPtrparentNode = do_QueryInterface(aParent); + + // remove aPropName from all intermediate nodes + nsCOMPtrsiblingNode; + nsCOMPtrnextSiblingNode; // temp to hold the next node in the list + result = aStartNode->GetNextSibling(getter_AddRefs(siblingNode)); + while (siblingNode && NS_SUCCEEDED(result)) + { + // get next sibling right away, before we move siblingNode! + siblingNode->GetNextSibling(getter_AddRefs(nextSiblingNode)); + if (aEndNode==siblingNode.get()) { // found the end node, handle that below + break; + } + else + { // found a sibling node between aStartNode and aEndNode, remove the style node + PRUint32 childCount=0; + nodeAsChar = do_QueryInterface(siblingNode); + if (nodeAsChar) { + nodeAsChar->GetLength(&childCount); + } + else + { + nsCOMPtrgrandChildNodes; + result = siblingNode->GetChildNodes(getter_AddRefs(grandChildNodes)); + if (NS_SUCCEEDED(result) && grandChildNodes) { + grandChildNodes->GetLength(&childCount); + } + if (0==childCount) + { // node has no children + // XXX: for now, I think that's ok. just pass in 0 + } + } + if (NS_SUCCEEDED(result)) { + siblingNode->GetParentNode(getter_AddRefs(parentNode)); + result = RemoveTextPropertiesForNode(siblingNode, parentNode, 0, childCount, aPropName, aAttribute); + } + } + siblingNode = do_QueryInterface(nextSiblingNode); + } + if (NS_SUCCEEDED(result)) + { + // remove aPropName from aStartNode + //nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(aStartNode); + if (nodeAsChar) { + nodeAsChar->GetLength((PRUint32 *)&endOffset); + } + else + { + if (gNoisy) { printf("not yet supported\n");} + return NS_ERROR_NOT_IMPLEMENTED; + } + result = aStartNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_SUCCEEDED(result)) { + result = RemoveTextPropertiesForNode(aStartNode, parentNode, startOffset, endOffset, aPropName, aAttribute); + } + } + if (NS_SUCCEEDED(result)) + { + // remove aPropName from the end node + startOffset = 0; + endOffset = aEndOffset; + result = aEndNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_SUCCEEDED(result)) { + result = RemoveTextPropertiesForNode(aEndNode, parentNode, startOffset, endOffset, aPropName, aAttribute); + } + } + return result; } NS_IMETHODIMP -nsHTMLEditor::StopLogging() +nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute) { - return nsTextEditor::StopLogging(); + if (gNoisy) { printf("start nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents\n"); } + nsresult result=NS_OK; + if (!aStartNode || !aEndNode || !aParent || !aPropName) + return NS_ERROR_NULL_POINTER; + + PRInt32 rangeStartOffset = aStartOffset; // used to construct a range for the nodes between + PRInt32 rangeEndOffset = aEndOffset; // aStartNode and aEndNode after we've processed those endpoints + + // temporary state variables + PRBool textPropertySet; + nsCOMPtrresultNode; + + // delete the style node for the text in the start parent + nsCOMPtrstartNode = do_QueryInterface(aStartNode); // use computed endpoint based on end points passed in + nsCOMPtrendNode = do_QueryInterface(aEndNode); // use computed endpoint based on end points passed in + nsCOMPtrparent; // the parent of the node we're operating on + PRBool skippedStartNode = PR_FALSE; // we skip the start node if aProp is not set on it + PRUint32 count; + nsCOMPtrnodeAsChar; + nodeAsChar = do_QueryInterface(startNode); + if (nodeAsChar) + { + result = aStartNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(result)) { return result; } + nodeAsChar->GetLength(&count); + if ((PRUint32)aStartOffset!=count) + { // only do this if at least one child is selected + IsTextPropertySetByContent(aStartNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); + if (PR_TRUE==textPropertySet) + { + result = RemoveTextPropertiesForNode(aStartNode, parent, aStartOffset, count, aPropName, aAttribute); + if (0!=aStartOffset) { + rangeStartOffset = 0; // we split aStartNode at aStartOffset and it is the right node now + } + } + else + { + skippedStartNode = PR_TRUE; + if (gNoisy) { printf("skipping start node because property not set\n"); } + } + } + else + { + skippedStartNode = PR_TRUE; + if (gNoisy) { printf("skipping start node because aStartOffset==count\n"); } + } + } + else + { + startNode = GetChildAt(aStartNode, aStartOffset); + parent = do_QueryInterface(aStartNode); + if (!startNode || !parent) {return NS_ERROR_NULL_POINTER;} + IsTextPropertySetByContent(startNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); + if (PR_TRUE==textPropertySet) + { + result = RemoveTextPropertiesForNode(startNode, parent, aStartOffset, aStartOffset+1, aPropName, aAttribute); + } + else + { + skippedStartNode = PR_TRUE; + if (gNoisy) { printf("skipping start node because property not set\n"); } + } + } + + // delete the style node for the text in the end parent + if (NS_SUCCEEDED(result)) + { + nodeAsChar = do_QueryInterface(endNode); + if (nodeAsChar) + { + result = endNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(result)) { return result; } + nodeAsChar->GetLength(&count); + if (aEndOffset!=0) + { // only do this if at least one child is selected + IsTextPropertySetByContent(endNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); + if (PR_TRUE==textPropertySet) + { + result = RemoveTextPropertiesForNode(endNode, parent, 0, aEndOffset, aPropName, aAttribute); + if (0!=aEndOffset) { + rangeEndOffset = 0; // we split endNode at aEndOffset and it is the right node now + } + } + else { if (gNoisy) { printf("skipping end node because aProperty not set.\n"); } } + } + else { if (gNoisy) { printf("skipping end node because aEndOffset==0\n"); } } + } + else + { + endNode = GetChildAt(aEndNode, aEndOffset); + parent = do_QueryInterface(aEndNode); + if (!endNode || !parent) {return NS_ERROR_NULL_POINTER;} + result = RemoveTextPropertiesForNode(endNode, parent, aEndOffset, aEndOffset+1, aPropName, aAttribute); + } + } + + // remove aPropName style nodes for all the content between the start and end nodes + if (NS_SUCCEEDED(result)) + { + // build our own range now, because the endpoints may have shifted during shipping + nsCOMPtr range; + result = nsComponentManager::CreateInstance(kCRangeCID, + nsnull, + nsIDOMRange::GetIID(), + getter_AddRefs(range)); + if (NS_FAILED(result)) { return result; } + if (!range) { return NS_ERROR_NULL_POINTER; } + // compute the start node + if (PR_TRUE==skippedStartNode) + { + nsCOMPtrtempNode = do_QueryInterface(startNode); + nsEditor::GetNextNode(startNode, PR_TRUE, getter_AddRefs(tempNode)); + startNode = do_QueryInterface(tempNode); + } + range->SetStart(startNode, rangeStartOffset); + range->SetEnd(endNode, rangeEndOffset); + if (gNoisy) + { + printf("created range [(%p,%d), (%p,%d)]\n", + startNode.get(), rangeStartOffset, + endNode.get(), rangeEndOffset); + } + + nsVoidArray nodeList; + nsCOMPtriter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + nsIContentIterator::GetIID(), getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + nsCOMPtrstartContent; + startContent = do_QueryInterface(startNode); + nsCOMPtrendContent; + endContent = do_QueryInterface(endNode); + if (startContent && endContent) + { + iter->Init(range); + nsCOMPtr content; + iter->CurrentNode(getter_AddRefs(content)); + nsAutoString propName; // the property we are removing + aPropName->ToString(propName); + while (NS_COMFALSE == iter->IsDone()) + { + if ((content.get() != startContent.get()) && + (content.get() != endContent.get())) + { + nsCOMPtrelement; + element = do_QueryInterface(content); + if (element) + { + nsString tag; + element->GetTagName(tag); + if (propName.EqualsIgnoreCase(tag)) + { + if (-1==nodeList.IndexOf(content.get())) { + nodeList.AppendElement((void *)(content.get())); + } + } + } + } + // note we don't check the result, we just rely on iter->IsDone + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + } + } + + // now delete all the style nodes we found + if (NS_SUCCEEDED(result)) + { + nsIContent *contentPtr; + contentPtr = (nsIContent*)(nodeList.ElementAt(0)); + while (NS_SUCCEEDED(result) && contentPtr) + { + nsCOMPtrstyleNode; + styleNode = do_QueryInterface(contentPtr); + // promote the children of styleNode + nsCOMPtrparentNode; + result = styleNode->GetParentNode(getter_AddRefs(parentNode)); + if (NS_SUCCEEDED(result) && parentNode) + { + PRInt32 position; + result = GetChildOffset(styleNode, parentNode, position); + if (NS_SUCCEEDED(result)) + { + nsCOMPtrpreviousSiblingNode; + nsCOMPtrchildNode; + result = styleNode->GetLastChild(getter_AddRefs(childNode)); + while (NS_SUCCEEDED(result) && childNode) + { + childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); + // explicitly delete of childNode from styleNode + // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! + result = nsEditor::DeleteNode(childNode); + if (NS_SUCCEEDED(result)) + { + result = nsEditor::InsertNode(childNode, parentNode, position); + if (gNoisy) + { + printf("deleted next sibling node %p\n", childNode.get()); + DebugDumpContent(); // DEBUG + } + } + childNode = do_QueryInterface(previousSiblingNode); + } // end while loop + // delete styleNode + result = nsEditor::DeleteNode(styleNode); + if (gNoisy) + { + printf("deleted style node %p\n", styleNode.get()); + DebugDumpContent(); // DEBUG + } + } + } + + // get next content ptr + nodeList.RemoveElementAt(0); + contentPtr = (nsIContent*)(nodeList.ElementAt(0)); + } + } + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result)) + { + selection->Collapse(startNode, rangeStartOffset); + selection->Extend(endNode, rangeEndOffset); + if (gNoisy) { printf("RTPFNWDP set selection.\n"); } + } + } + if (gNoisy) + { + printf("end nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents, dumping content...\n"); + DebugDumpContent(); + } + + return result; } diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h index 38807d259708..51b1a0e0fdc6 100644 --- a/editor/libeditor/html/nsHTMLEditor.h +++ b/editor/libeditor/html/nsHTMLEditor.h @@ -19,21 +19,33 @@ #ifndef nsHTMLEditor_h__ #define nsHTMLEditor_h__ -#include "nsITextEditor.h" -#include "nsTextEditor.h" -#include "nsIHTMLEditor.h" #include "nsCOMPtr.h" + +#include "nsIHTMLEditor.h" +#include "nsITableEditor.h" +#include "nsIEditorMailSupport.h" +#include "nsIEditorStyleSheets.h" + +#include "nsEditor.h" +#include "nsIDOMElement.h" #include "nsIDOMEventListener.h" #include "nsITableLayout.h" +#include "TypeInState.h" +#include "nsEditRules.h" + /** * The HTML editor implementation.
* Use to edit HTML document represented as a DOM tree. */ -class nsHTMLEditor : public nsTextEditor, public nsIHTMLEditor +class nsHTMLEditor : public nsEditor, + public nsIHTMLEditor, + public nsIEditorMailSupport, + public nsITableEditor, + public nsIEditorStyleSheets { - typedef enum {eNoOp=0, eReplaceParent=1, eInsertParent=2} BlockTransformationType; + typedef enum {eNoOp, eReplaceParent=1, eInsertParent=2} BlockTransformationType; public: // see nsIHTMLEditor for documentation @@ -43,139 +55,77 @@ public: // another class. Only the base class should use NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS_INHERITED - nsHTMLEditor(); - virtual ~nsHTMLEditor(); + nsHTMLEditor(); + virtual ~nsHTMLEditor(); -//Initialization - NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell); + /* ------------ nsIHTMLEditor methods -------------- */ -//============================================================================ -// Methods that are duplicates of nsTextEditor -- exposed here for convenience + NS_IMETHOD GetDocumentLength(PRInt32 *aCount); + NS_IMETHOD SetMaxTextLength(PRInt32 aMaxTextLength); + NS_IMETHOD GetMaxTextLength(PRInt32& aMaxTextLength); -// Editing Operations - NS_IMETHOD SetTextProperty(nsIAtom *aProperty, + + NS_IMETHOD SetInlineProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue); - NS_IMETHOD GetTextProperty(nsIAtom *aProperty, - const nsString *aAttribute, const nsString *aValue, + + NS_IMETHOD GetInlineProperty(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, PRBool &aFirst, PRBool &aAny, PRBool &aAll); - NS_IMETHOD RemoveTextProperty(nsIAtom *aProperty, const nsString *aAttribute); - NS_IMETHOD DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction); - NS_IMETHOD InsertText(const nsString& aStringToInsert); + + NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute); + NS_IMETHOD InsertBreak(); - NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); - -// Transaction control - NS_IMETHOD EnableUndo(PRBool aEnable); - NS_IMETHOD Undo(PRUint32 aCount); - NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo); - NS_IMETHOD Redo(PRUint32 aCount); - NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo); - NS_IMETHOD BeginTransaction(); - NS_IMETHOD EndTransaction(); - -// Selection and navigation - NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection); - NS_IMETHOD SelectAll(); - NS_IMETHOD BeginningOfDocument(); - NS_IMETHOD EndOfDocument(); - NS_IMETHOD ScrollUp(nsIAtom *aIncrement); - NS_IMETHOD ScrollDown(nsIAtom *aIncrement); - NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin); - -// file handling - NS_IMETHOD Save(); - NS_IMETHOD SaveAs(PRBool aSavingCopy); - -// cut, copy & paste - NS_IMETHOD Cut(); - NS_IMETHOD Copy(); - NS_IMETHOD Paste(); - NS_IMETHOD PasteAsQuotation(); - NS_IMETHOD PasteAsCitedQuotation(const nsString& aCitation); - NS_IMETHOD InsertAsQuotation(const nsString& aQuotedText); - NS_IMETHOD InsertAsCitedQuotation(const nsString& aQuotedText, - const nsString& aCitation); - -// Input/Output - NS_IMETHOD InsertHTML(const nsString& aInputString); - - NS_IMETHOD BeginComposition(void); - NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRange, nsTextEventReply* aReply); - NS_IMETHOD EndComposition(void); - - NS_IMETHOD OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags); - NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags); - -// Miscellaneous - NS_IMETHOD ApplyStyleSheet(const nsString& aURL); - -// Logging methods - - NS_IMETHOD StartLogging(nsIFileSpec *aLogFile); - NS_IMETHOD StopLogging(); - -// End of methods implemented in nsEditor -//============================================================= -// HTML Editing methods - - // This sets background on the appropriate container element (table, cell,) - // or calls into nsTextEditor to set the page background - NS_IMETHOD SetBackgroundColor(const nsString& aColor); - NS_IMETHOD SetBodyAttribute(const nsString& aAttr, const nsString& aValue); - NS_IMETHOD GetParagraphStyle(nsStringArray *aTagList); - NS_IMETHOD AddBlockParent(nsString& aParentTag); - NS_IMETHOD ReplaceBlockParent(nsString& aParentTag); - NS_IMETHOD RemoveParagraphStyle(); - NS_IMETHOD RemoveParent(const nsString &aParentTag); + NS_IMETHOD InsertText(const nsString& aStringToInsert); + NS_IMETHOD InsertHTML(const nsString &aInputString); + NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection); + + NS_IMETHOD DeleteSelection(ESelectionCollapseDirection aAction); + NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode); + NS_IMETHOD SelectElement(nsIDOMElement* aElement); + NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement); NS_IMETHOD GetParagraphFormat(nsString& aParagraphFormat); NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat); + NS_IMETHOD GetParagraphStyle(nsStringArray *aTagList); + NS_IMETHOD RemoveParagraphStyle(); + + NS_IMETHOD AddBlockParent(nsString& aParentTag); + NS_IMETHOD ReplaceBlockParent(nsString& aParentTag); + NS_IMETHOD RemoveParent(const nsString &aParentTag); + + NS_IMETHOD InsertList(const nsString& aListType); NS_IMETHOD Indent(const nsString& aIndent); NS_IMETHOD Align(const nsString& aAlign); - NS_IMETHOD InsertList(const nsString& aListType); - -// MHTML helper methods - NS_IMETHOD GetEmbeddedObjects(nsISupportsArray** aNodeList); NS_IMETHOD GetElementOrParentByTagName(const nsString& aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn); NS_IMETHOD GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn); NS_IMETHOD CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement** aReturn); - NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection); + NS_IMETHOD SaveHLineSettings(nsIDOMElement* aElement); NS_IMETHOD InsertLinkAroundSelection(nsIDOMElement* aAnchorElement); - NS_IMETHOD SelectElement(nsIDOMElement* aElement); - NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement); - // Return TRUE if aElement is a table-related elemet and caret was set - PRBool SetCaretInTableCell(nsIDOMElement* aElement); - PRBool IsElementInBody(nsIDOMElement* aElement); -// Table Editing (implemented in EditTable.cpp) - // Helper used to get nsITableLayout interface for methods implemented in nsTableFrame - NS_IMETHOD GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **tableLayoutObject); - /** Get the row an column index from the layout's cellmap */ - NS_IMETHOD GetCellIndexes(nsIDOMElement *aCell, PRInt32 & aRowIndex, PRInt32 &aColIndex); - /** Get the number of rows and columns in a table from the layout's cellmap */ - NS_IMETHOD GetTableSize(nsIDOMElement *aTable, PRInt32& aRowCount, PRInt32& aColCount); + /* ------------ nsIEditorStyleSheets methods -------------- */ - /* Get cell at a cellmap location. Returns NS_TABLELAYOUT_CELL_NOT_FOUND if past end of row or col */ - NS_IMETHOD GetCellAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell); - /* Get cell and associated data */ - NS_IMETHOD GetCellDataAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell, - PRInt32& aStartRowIndex, PRInt32& aStartColIndex, - PRInt32& aRowSpan, PRInt32& aColSpan, PRBool& aIsSelected); + NS_IMETHOD ApplyStyleSheet(const nsString& aURL); + NS_IMETHOD AddStyleSheet(nsICSSStyleSheet* aSheet); + NS_IMETHOD RemoveStyleSheet(nsICSSStyleSheet* aSheet); + + /* ------------ nsIEditorMailSupport methods -------------- */ + + NS_IMETHOD GetBodyWrapWidth(PRInt32 *aWrapColumn); + NS_IMETHOD SetBodyWrapWidth(PRInt32 aWrapColumn); + NS_IMETHOD PasteAsQuotation(); + NS_IMETHOD InsertAsQuotation(const nsString& aQuotedText); + NS_IMETHOD PasteAsCitedQuotation(const nsString& aCitation); + NS_IMETHOD InsertAsCitedQuotation(const nsString& aQuotedText, const nsString& aCitation); + NS_IMETHOD GetEmbeddedObjects(nsISupportsArray** aNodeList); + + + /* ------------ nsITableEditor methods -------------- */ NS_IMETHOD InsertTableCell(PRInt32 aNumber, PRBool aAfter); NS_IMETHOD InsertTableColumn(PRInt32 aNumber, PRBool aAfter); @@ -185,15 +135,85 @@ public: NS_IMETHOD DeleteTableColumn(PRInt32 aNumber); NS_IMETHOD DeleteTableRow(PRInt32 aNumber); NS_IMETHOD JoinTableCells(); - - /** Make table "rectangular" -- fill in all missing cellmap locations - * If aTable is null, it uses table enclosing the selection anchor - */ NS_IMETHOD NormalizeTable(nsIDOMElement *aTable); + NS_IMETHOD GetCellIndexes(nsIDOMElement *aCell, PRInt32& aRowIndex, PRInt32& aColIndex); + NS_IMETHOD GetTableSize(nsIDOMElement *aTable, PRInt32& aRowCount, PRInt32& aColCount); + NS_IMETHOD GetCellAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell); + NS_IMETHOD GetCellDataAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell, + PRInt32& aStartRowIndex, PRInt32& aStartColIndex, + PRInt32& aRowSpan, PRInt32& aColSpan, PRBool& aIsSelected); + + +// Selection and navigation + /* obsolete + NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection); + NS_IMETHOD ScrollUp(nsIAtom *aIncrement); + NS_IMETHOD ScrollDown(nsIAtom *aIncrement); + NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin); + */ + + /* miscellaneous */ + // This sets background on the appropriate container element (table, cell,) + // or calls into nsTextEditor to set the page background + NS_IMETHOD SetBackgroundColor(const nsString& aColor); + NS_IMETHOD SetBodyAttribute(const nsString& aAttr, const nsString& aValue); + + + + /* ------------ Overrides of nsEditor interface methods -------------- */ + + NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, PRUint32 aFlags); + + NS_IMETHOD GetFlags(PRUint32 *aFlags); + NS_IMETHOD SetFlags(PRUint32 aFlags); + + NS_IMETHOD Cut(); + NS_IMETHOD Copy(); + NS_IMETHOD Paste(); + + NS_IMETHOD OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags); + + NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags); + + NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); + +protected: + + virtual void InitRules(); + + NS_IMETHOD GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject); + + NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parentSelectedNode, PRInt32& offsetOfNewNode); + + /* StyleSheet load callback */ + static void ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData); + + /* remove the old style sheet, and apply the supplied one */ + NS_IMETHOD ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet); + + + // Return TRUE if aElement is a table-related elemet and caret was set + PRBool SetCaretInTableCell(nsIDOMElement* aElement); + PRBool IsElementInBody(nsIDOMElement* aElement); + +// Table Editing (implemented in EditTable.cpp) + + // Helper used to get nsITableLayout interface for methods implemented in nsTableFrame + NS_IMETHOD GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **tableLayoutObject); // Table utilities -// All of the above need to get the same basic context data + // All of the above need to get the same basic context data NS_IMETHOD GetCellContext(nsCOMPtr &aSelection, nsCOMPtr &aTable, nsCOMPtr &aCell, nsCOMPtr &aCellParent, PRInt32& aCellOffset, @@ -204,15 +224,10 @@ public: // Setting caret to a logical place can get tricky, // especially after deleting table stuff - typedef enum { ePreviousColumn=0, ePreviousRow } SetCaretSearchDirection; + typedef enum { ePreviousColumn, ePreviousRow } SetCaretSearchDirection; + NS_IMETHOD SetCaretAfterTableEdit(nsIDOMElement* aTable, PRInt32 aCol, PRInt32 aRow, SetCaretSearchDirection aDirection); - -protected: - -// rules initialization - - virtual void InitRules(); - + NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag, BlockTransformationType aTranformation); @@ -241,14 +256,178 @@ protected: NS_IMETHOD IsSubordinateBlock(nsString &aTag, PRBool &aIsTag); -// EVENT LISTENERS AND COMMAND ROUTING NEEDS WORK -// For now, the listners are tied to the nsTextEditor class -// -// nsCOMPtr mKeyListenerP; -// nsCOMPtr mMouseListenerP; + /** content-based query returns PR_TRUE if effects aNode + * If contains aNode, + * but also contains aNode and the second is + * more deeply nested than the first, then the first does not effect aNode. + * + * @param aNode The target of the query + * @param aProperty The property that we are querying for + * @param aAttribute The attribute of aProperty, example: color in + * May be null. + * @param aValue The value of aAttribute, example: blue in + * May be null. Ignored if aAttribute is null. + * @param aIsSet [OUT] PR_TRUE if effects aNode. + * @param aStyleNode [OUT] set to the node representing , if found. + * null if aIsSet is returned as PR_FALSE; + */ + virtual void IsTextPropertySetByContent(nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aIsSet, + nsIDOMNode **aStyleNode) const; - // this overrides the base class implementation. It is not exported in nsIHTMLEditor. - NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed); + /** 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. + */ + virtual void IsTextStyleSet(nsIStyleContext *aSC, + nsIAtom *aProperty, + const nsString *aAttributes, + PRBool &aIsSet) const; + + /** Moves the content between (aNode, aStartOffset) and (aNode, aEndOffset) + * into aNewParentNode, splitting aNode as necessary to maintain the relative + * position of all leaf content. + * @param aNode The node whose content we're repositioning. + * aNode can be either a text node or a container node. + * @param aNewParentNode The node that will be the repositioned contents' parent. + * The caller is responsible for allocating aNewParentNode + * @param aStartOffset The start offset of the content of aNode + * @param aEndOffset The end offset of the content of aNode. + */ + NS_IMETHOD MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, + nsIDOMNode *aNewParentNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset); + + /** Moves the content between (aStartNode, aStartOffset) and (aEndNode, aEndOffset) + * into aNewParentNode, splitting aStartNode and aEndNode as necessary to maintain + * the relative position of all leaf content. + * The content between the two endpoints MUST be "contiguous" in the sense that + * it is all in the same block. Another way of saying this is all content nodes + * between aStartNode and aEndNode must be inline. + * @see IntermediateNodesAreInline + * + * @param aStartNode The left node, can be either a text node or a container node. + * @param aStartOffset The start offset in the content of aStartNode + * @param aEndNode The right node, can be either a text node or a container node. + * @param aEndOffset The end offset in the content of aEndNode. + * @param aGrandParentNode The common ancestor of aStartNode and aEndNode. + * aGrandParentNode will be the parent of aNewParentNode. + * @param aNewParentNode The node that will be the repositioned contents' parent. + * The caller is responsible for allocating aNewParentNode + */ + NS_IMETHOD MoveContiguousContentIntoNewParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aGrandParentNode, + nsIDOMNode *aNewParentNode); + + + NS_IMETHOD SetTextPropertiesForNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue); + + NS_IMETHOD SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue); + + NS_IMETHOD SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, + nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue); + + NS_IMETHOD RemoveTextPropertiesForNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aPropName, + const nsString *aAttribute); + + NS_IMETHOD RemoveTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute); + + NS_IMETHOD RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + nsIAtom *aPropName, + const nsString *aAttribute); + + NS_IMETHOD SetTypeInStateForProperty(TypeInState &aTypeInState, + nsIAtom *aPropName, + const nsString *aAttribute, + const nsString *aValue); + + void GetTextSelectionOffsetsForRange(nsIDOMSelection *aSelection, + nsIDOMNode **aParent, + PRInt32 &aStartOffset, + PRInt32 &aEndOffset); + + void ResetTextSelectionForRange(nsIDOMNode *aParent, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIDOMSelection *aSelection); + + /** returns the absolute position of the end points of aSelection + * in the document as a text stream. + */ + nsresult GetTextSelectionOffsets(nsIDOMSelection *aSelection, + PRInt32 &aStartOffset, + PRInt32 &aEndOffset); + + // Methods for handling plaintext quotations + NS_IMETHOD PasteAsPlaintextQuotation(); + NS_IMETHOD InsertAsPlaintextQuotation(const nsString& aQuotedText); + + // I hate seeing nsCOMPtr return types. + nsCOMPtr FindPreElement(); + + TypeInState *GetTypeInState(); + + /** simple utility to handle any error with event listener allocation or registration */ + void HandleEventListenerError(); + +// Data members +protected: + + TypeInState* mTypeInState; + nsEditRules* mRules; + nsCOMPtr mKeyListenerP; + nsCOMPtr mMouseListenerP; + nsCOMPtr mTextListenerP; + nsCOMPtr mCompositionListenerP; + nsCOMPtr mDragListenerP; + nsCOMPtr mFocusListenerP; + PRBool mIsComposing; + PRInt32 mMaxTextLength; + PRUint32 mWrapColumn; + +// friends +friend class nsHTMLEditRules; +friend class nsTextEditRules; }; diff --git a/editor/libeditor/text/nsEditorEventListeners.cpp b/editor/libeditor/text/nsEditorEventListeners.cpp index 70675019803d..7ac517663e53 100644 --- a/editor/libeditor/text/nsEditorEventListeners.cpp +++ b/editor/libeditor/text/nsEditorEventListeners.cpp @@ -17,6 +17,9 @@ */ #include "nsEditorEventListeners.h" #include "nsEditor.h" +#include "nsVoidArray.h" +#include "nsString.h" + #include "nsIDOMDocument.h" #include "nsIDocument.h" #include "nsIPresShell.h" @@ -25,16 +28,11 @@ #include "nsIDOMCharacterData.h" #include "nsIEditProperty.h" #include "nsISupportsArray.h" -#include "nsVoidArray.h" -#include "nsString.h" #include "nsIStringStream.h" #include "nsIDOMUIEvent.h" #include "nsIDOMNSUIEvent.h" #include "nsIPrivateTextEvent.h" - -// for testing only -#include "nsIHTMLEditor.h" -// end for testing only +#include "nsIEditorMailSupport.h" // for repainting hack only #include "nsIView.h" @@ -142,7 +140,7 @@ nsTextEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) // break; case nsIDOMUIEvent::VK_DELETE: - mEditor->DeleteSelection(nsIEditor::eDeleteRight); + mEditor->DeleteSelection(nsIEditor::eDeleteNext); break; // case nsIDOMUIEvent::VK_RETURN: @@ -181,7 +179,7 @@ nsTextEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) { PRUint32 flags=0; mEditor->GetFlags(&flags); - if (! (flags & TEXT_EDITOR_FLAG_SINGLELINE)) + if (! (flags & nsIHTMLEditor::eEditorSingleLineMask)) { PRBool ctrlKey, altKey, metaKey; uiEvent->GetCtrlKey(&ctrlKey); @@ -192,7 +190,10 @@ nsTextEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) // else we insert the tab straight through nsAutoString key; key += keyCode; - mEditor->InsertText(key); + + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) + htmlEditor->InsertText(key); return NS_ERROR_BASE; // this means "I handled the event, don't do default processing" } else { @@ -244,14 +245,17 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) if (metaKey) return NS_OK; // don't consume + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (!htmlEditor) return NS_ERROR_NO_INTERFACE; + if (NS_SUCCEEDED(uiEvent->GetKeyCode(&keyCode))) { if (nsIDOMUIEvent::VK_BACK==keyCode) { - mEditor->DeleteSelection(nsIEditor::eDeleteLeft); + mEditor->DeleteSelection(nsIEditor::eDeletePrevious); return NS_ERROR_BASE; // consumed } if (nsIDOMUIEvent::VK_RETURN==keyCode) { - mEditor->InsertBreak(); + htmlEditor->InsertBreak(); return NS_ERROR_BASE; // consumed } } @@ -263,7 +267,7 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) return NS_OK; // ignore tabs here, they're handled in keyDown if at all } key += character; - mEditor->InsertText(key); + htmlEditor->InsertText(key); } return NS_ERROR_BASE; // consumed @@ -333,28 +337,6 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr format = "text/html"; res = mEditor->OutputToString(output, format, nsEditor::EditorOutputFormatted); -#if 0 - nsCOMPtr htmlEditor (do_QueryInterface(mEditor)); - if (htmlEditor) - { - if (isShift) - res = htmlEditor->OutputTextToString(output, PR_TRUE, PR_FALSE); - else - res = htmlEditor->OutputHTMLToString(output, PR_FALSE); - } - else - { - nsCOMPtr textEditor (do_QueryInterface(mEditor)); - if (textEditor) - { - if (isShift) - res = textEditor->OutputTextToString(output, PR_TRUE, PR_FALSE); - else - res = textEditor->OutputHTMLToString(output, PR_FALSE); - } - } -#endif - if (NS_SUCCEEDED(res)) { char* buf = output.ToNewCString(); @@ -378,11 +360,15 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr { printf("Getting number of columns\n"); aProcessed=PR_TRUE; - PRInt32 wrap; - if (NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - printf("Currently wrapping to %d\n", wrap); - else - printf("GetBodyWrapWidth returned an error\n"); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + { + PRInt32 wrap; + if (NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + printf("Currently wrapping to %d\n", wrap); + else + printf("GetBodyWrapWidth returned an error\n"); + } } break; @@ -390,20 +376,24 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr // hard coded "Decrease wrap size" if (PR_TRUE==altKey) { - aProcessed=PR_TRUE; - PRInt32 wrap; - if (!NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - { - printf("GetBodyWrapWidth returned an error\n"); - break; - } - mEditor->SetBodyWrapWidth(wrap - 5); - if (!NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - { - printf("Second GetBodyWrapWidth returned an error\n"); - break; - } - else printf("Now wrapping to %d\n", wrap); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + { + aProcessed=PR_TRUE; + PRInt32 wrap; + if (!NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + { + printf("GetBodyWrapWidth returned an error\n"); + break; + } + mailEditor->SetBodyWrapWidth(wrap - 5); + if (!NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + { + printf("Second GetBodyWrapWidth returned an error\n"); + break; + } + else printf("Now wrapping to %d\n", wrap); + } } break; @@ -411,20 +401,24 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr // hard coded "Increase wrap size" if (PR_TRUE==altKey) { - aProcessed=PR_TRUE; - PRInt32 wrap; - if (!NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - { - printf("GetBodyWrapWidth returned an error\n"); - break; - } - mEditor->SetBodyWrapWidth(wrap + 5); - if (!NS_SUCCEEDED(mEditor->GetBodyWrapWidth(&wrap))) - { - printf("Second GetBodyWrapWidth returned an error\n"); - break; - } - else printf("Now wrapping to %d\n", wrap); + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + { + aProcessed=PR_TRUE; + PRInt32 wrap; + if (!NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + { + printf("GetBodyWrapWidth returned an error\n"); + break; + } + mailEditor->SetBodyWrapWidth(wrap + 5); + if (!NS_SUCCEEDED(mailEditor->GetBodyWrapWidth(&wrap))) + { + printf("Second GetBodyWrapWidth returned an error\n"); + break; + } + else printf("Now wrapping to %d\n", wrap); + } } break; @@ -436,7 +430,11 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr if (mEditor) { if (altKey) - mEditor->PasteAsQuotation(); + { + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + mailEditor->PasteAsQuotation(); + } else mEditor->Paste(); } @@ -465,19 +463,20 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_I: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) PRBool any = PR_FALSE; PRBool all = PR_FALSE; PRBool first = PR_FALSE; - mEditor->GetTextProperty(nsIEditProperty::i, nsnull, nsnull, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::i, nsnull, nsnull, first, any, all); if (PR_FALSE==first) { - mEditor->SetTextProperty(nsIEditProperty::i, nsnull, nsnull); + htmlEditor->SetInlineProperty(nsIEditProperty::i, nsnull, nsnull); } else { - mEditor->RemoveTextProperty(nsIEditProperty::i, nsnull); + htmlEditor->RemoveInlineProperty(nsIEditProperty::i, nsnull); } } } @@ -486,7 +485,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr else if (PR_TRUE==altKey) { aProcessed=PR_TRUE; - nsCOMPtr htmlEditor (do_QueryInterface(mEditor)); + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); if (htmlEditor) { nsString nsstr ("This is bold and emphasized text"); @@ -499,19 +498,20 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_B: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) PRBool any = PR_FALSE; PRBool all = PR_FALSE; PRBool first = PR_FALSE; - mEditor->GetTextProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::b, nsnull, nsnull, first, any, all); if (PR_FALSE==first) { - mEditor->SetTextProperty(nsIEditProperty::b, nsnull, nsnull); + htmlEditor->SetInlineProperty(nsIEditProperty::b, nsnull, nsnull); } else { - mEditor->RemoveTextProperty(nsIEditProperty::b, nsnull); + htmlEditor->RemoveInlineProperty(nsIEditProperty::b, nsnull); } } } @@ -521,19 +521,20 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_U: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) PRBool any = PR_FALSE; PRBool all = PR_FALSE; PRBool first = PR_FALSE; - mEditor->GetTextProperty(nsIEditProperty::u, nsnull, nsnull, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::u, nsnull, nsnull, first, any, all); if (PR_FALSE==first) { - mEditor->SetTextProperty(nsIEditProperty::u, nsnull, nsnull); + htmlEditor->SetInlineProperty(nsIEditProperty::u, nsnull, nsnull); } else { - mEditor->RemoveTextProperty(nsIEditProperty::u, nsnull); + htmlEditor->RemoveInlineProperty(nsIEditProperty::u, nsnull); } } @@ -544,7 +545,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_1: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -553,9 +555,9 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr PRBool first = PR_FALSE; nsAutoString color = "COLOR"; nsAutoString value = "red"; - mEditor->GetTextProperty(nsIEditProperty::font, &color, &value, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::font, &color, &value, first, any, all); if (!all) { - mEditor->SetTextProperty(nsIEditProperty::font, &color, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &color, &value); } else { printf("NOOP: all selected text is already red\n"); @@ -568,7 +570,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_2: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -576,9 +579,9 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr PRBool all = PR_FALSE; PRBool first = PR_FALSE; nsAutoString color = "COLOR"; - mEditor->GetTextProperty(nsIEditProperty::font, &color, nsnull, first, any, all); + htmlEditor->GetInlineProperty(nsIEditProperty::font, &color, nsnull, first, any, all); if (any) { - mEditor->RemoveTextProperty(nsIEditProperty::font, &color); + htmlEditor->RemoveInlineProperty(nsIEditProperty::font, &color); } else { printf("NOOP: no color set\n"); @@ -591,7 +594,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_3: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -600,7 +604,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr //PRBool first = PR_FALSE; nsAutoString prop = "SIZE"; nsAutoString value = "+2"; - mEditor->SetTextProperty(nsIEditProperty::font, &prop, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &prop, &value); } } break; @@ -609,7 +613,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_4: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -618,7 +623,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr //PRBool first = PR_FALSE; nsAutoString prop = "SIZE"; nsAutoString value = "-2"; - mEditor->SetTextProperty(nsIEditProperty::font, &prop, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &prop, &value); } } break; @@ -627,7 +632,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_5: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -636,7 +642,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr //PRBool first = PR_FALSE; nsAutoString prop = "FACE"; nsAutoString value = "helvetica"; - mEditor->SetTextProperty(nsIEditProperty::font, &prop, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &prop, &value); } } break; @@ -645,7 +651,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_6: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { // XXX: move this logic down into texteditor rules delegate // should just call mEditor->ChangeTextProperty(prop) @@ -654,7 +661,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr //PRBool first = PR_FALSE; nsAutoString prop = "FACE"; nsAutoString value = "times"; - mEditor->SetTextProperty(nsIEditProperty::font, &prop, &value); + htmlEditor->SetInlineProperty(nsIEditProperty::font, &prop, &value); } } break; @@ -663,16 +670,12 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_7: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tag; - nsIEditProperty::h1->ToString(tag); - htmlEditor->ReplaceBlockParent(tag); - } + nsAutoString tag; + nsIEditProperty::h1->ToString(tag); + htmlEditor->ReplaceBlockParent(tag); } } break; @@ -681,16 +684,12 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_8: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tag; - nsIEditProperty::h2->ToString(tag); - htmlEditor->ReplaceBlockParent(tag); - } + nsAutoString tag; + nsIEditProperty::h2->ToString(tag); + htmlEditor->ReplaceBlockParent(tag); } } break; @@ -699,13 +698,9 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_9: if (PR_TRUE==ctrlKey) { - if (mEditor) - { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) { - htmlEditor->RemoveParagraphStyle(); - } + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { + htmlEditor->RemoveParagraphStyle(); } } break; @@ -714,28 +709,23 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_0: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) + printf("testing GetParagraphStyle\n"); + nsStringArray styles; + nsresult result = htmlEditor->GetParagraphStyle(&styles); + if (NS_SUCCEEDED(result)) { - printf("testing GetParagraphStyle\n"); - nsStringArray styles; - nsresult result = htmlEditor->GetParagraphStyle(&styles); - if (NS_SUCCEEDED(result)) + PRInt32 count = styles.Count(); + PRInt32 i; + for (i=0; iToNewCString(); - printf("%s ", tagCString); - delete [] tagCString; - } - printf("\n"); + nsString *tag = styles.StringAt(i); + char *tagCString = tag->ToNewCString(); + printf("%s ", tagCString); + delete [] tagCString; } + printf("\n"); } } } @@ -745,16 +735,12 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_COMMA: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tag; - nsIEditProperty::blockquote->ToString(tag); - htmlEditor->AddBlockParent(tag); - } + nsAutoString tag; + nsIEditProperty::blockquote->ToString(tag); + htmlEditor->AddBlockParent(tag); } } break; @@ -763,16 +749,12 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr case nsIDOMUIEvent::VK_PERIOD: if (PR_TRUE==ctrlKey) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsCOMPtrhtmlEditor; - htmlEditor = do_QueryInterface(mEditor); - if (htmlEditor) - { - nsAutoString tag; - nsIEditProperty::blockquote->ToString(tag); - htmlEditor->RemoveParent(tag); - } + nsAutoString tag; + nsIEditProperty::blockquote->ToString(tag); + htmlEditor->RemoveParent(tag); } } break; @@ -907,8 +889,11 @@ nsTextEditorMouseListener::MouseDown(nsIDOMEvent* aMouseEvent) uiEvent->GetCtrlKey(&ctrlKey); if (ctrlKey) - return editor->PasteAsQuotation(); - + { + nsCOMPtr mailEditor = do_QueryInterface(mEditor); + if (mailEditor) + mailEditor->PasteAsQuotation(); + } return editor->Paste(); } @@ -933,24 +918,21 @@ nsTextEditorMouseListener::MouseClick(nsIDOMEvent* aMouseEvent) nsresult nsTextEditorMouseListener::MouseDblClick(nsIDOMEvent* aMouseEvent) { - if (mEditor) + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) { - nsIHTMLEditor * HTMLEditor; - if (NS_SUCCEEDED(mEditor->QueryInterface(nsIHTMLEditor::GetIID(), (void**)&HTMLEditor))) + nsCOMPtr selectedElement; + if (NS_SUCCEEDED(htmlEditor->GetSelectedElement("", getter_AddRefs(selectedElement))) && selectedElement) { - nsCOMPtr selectedElement; - if (NS_SUCCEEDED(HTMLEditor->GetSelectedElement("", getter_AddRefs(selectedElement))) && selectedElement) - { - nsAutoString TagName; - selectedElement->GetTagName(TagName); - TagName.ToLowerCase(); + nsAutoString TagName; + selectedElement->GetTagName(TagName); + TagName.ToLowerCase(); #if DEBUG_cmanske - char szTagName[64]; - TagName.ToCString(szTagName, 64); - printf("Single Selected element found: %s\n", szTagName); + char szTagName[64]; + TagName.ToCString(szTagName, 64); + printf("Single Selected element found: %s\n", szTagName); #endif - } } } return NS_OK; @@ -1035,7 +1017,7 @@ nsresult nsTextEditorTextListener::HandleText(nsIDOMEvent* aTextEvent) { nsString composedText; - nsresult result; + nsresult result = NS_OK; nsCOMPtr textEvent; nsIPrivateTextRangeList *textRangeList; nsTextEventReply *textEventReply; @@ -1050,7 +1032,9 @@ nsTextEditorTextListener::HandleText(nsIDOMEvent* aTextEvent) textEvent->GetInputRange(&textRangeList); textEvent->GetEventReply(&textEventReply); textRangeList->AddRef(); - result = mEditor->SetCompositionString(composedText,textRangeList,textEventReply); + nsCOMPtr imeEditor = do_QueryInterface(mEditor, &result); + if (imeEditor) + result = imeEditor->SetCompositionString(composedText,textRangeList,textEventReply); return result; } @@ -1205,9 +1189,12 @@ nsTextEditorDragListener::DragDrop(nsIDOMEvent* aMouseEvent) trans->GetAnyTransferData(&textMime, (void **)&str, &len); // If the string was not empty then paste it in - if (str) { + if (str) + { + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); stuffToPaste.SetString(str, len); - mEditor->InsertText(stuffToPaste); + if (htmlEditor) + htmlEditor->InsertText(stuffToPaste); dragSession->SetCanDrop(PR_TRUE); } @@ -1269,6 +1256,16 @@ nsTextEditorCompositionListener::HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; } + +void nsTextEditorCompositionListener::SetEditor(nsIEditor *aEditor) +{ + nsCOMPtr imeEditor = do_QueryInterface(aEditor); + if (!imeEditor) return; // should return an error here! + + // note that we don't hold an extra reference here. + mEditor = imeEditor; +} + nsresult nsTextEditorCompositionListener::HandleStartComposition(nsIDOMEvent* aCompositionEvent) { @@ -1291,7 +1288,7 @@ nsTextEditorCompositionListener::HandleEndComposition(nsIDOMEvent* aCompositionE nsresult NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, - nsITextEditor *aEditor) + nsIEditor *aEditor) { nsTextEditorKeyListener* it = new nsTextEditorKeyListener(); if (nsnull == it) { @@ -1307,7 +1304,7 @@ NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, nsresult NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, - nsITextEditor *aEditor) + nsIEditor *aEditor) { nsTextEditorMouseListener* it = new nsTextEditorMouseListener(); if (nsnull == it) { @@ -1321,7 +1318,7 @@ NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsresult -NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor* aEditor) +NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor* aEditor) { nsTextEditorTextListener* it = new nsTextEditorTextListener(); if (nsnull==it) { @@ -1337,7 +1334,7 @@ NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor nsresult NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, - nsITextEditor *aEditor) + nsIEditor *aEditor) { nsTextEditorDragListener* it = new nsTextEditorDragListener(); if (nsnull == it) { @@ -1350,7 +1347,7 @@ NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, } nsresult -NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor* aEditor) +NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor* aEditor) { nsTextEditorCompositionListener* it = new nsTextEditorCompositionListener(); if (nsnull==it) { @@ -1362,7 +1359,7 @@ NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsITex nsresult NS_NewEditorFocusListener(nsIDOMEventListener ** aInstancePtrResult, - nsITextEditor *aEditor) + nsIEditor *aEditor) { nsTextEditorFocusListener* it = new nsTextEditorFocusListener(); if (nsnull == it) { @@ -1428,12 +1425,15 @@ nsTextEditorFocusListener::HandleEvent(nsIDOMEvent* aEvent) nsresult nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) { + // turn on selection and caret + if (mEditor) + { // turn on selection and caret if (mEditor) { PRUint32 flags; mEditor->GetFlags(&flags); - if (! (flags & TEXT_EDITOR_FLAG_DISABLED)) + if (! (flags & nsIHTMLEditor::eEditorDisabledMask)) { // only enable caret and selection if the editor is not disabled nsCOMPtreditor = do_QueryInterface(mEditor); if (editor) @@ -1442,7 +1442,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) editor->GetPresShell(getter_AddRefs(ps)); if (ps) { - if (! (flags & TEXT_EDITOR_FLAG_READONLY)) + if (! (flags & nsIHTMLEditor::eEditorReadonlyMask)) { // only enable caret if the editor is not readonly ps->SetCaretEnabled(PR_TRUE); } @@ -1474,6 +1474,8 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent) } return NS_OK; } + return NS_OK; +} nsresult nsTextEditorFocusListener::Blur(nsIDOMEvent* aEvent) diff --git a/editor/libeditor/text/nsEditorEventListeners.h b/editor/libeditor/text/nsEditorEventListeners.h index bef8a19e5601..a0d3b7f7947b 100644 --- a/editor/libeditor/text/nsEditorEventListeners.h +++ b/editor/libeditor/text/nsEditorEventListeners.h @@ -19,6 +19,9 @@ #ifndef editorInterfaces_h__ #define editorInterfaces_h__ +#include "nsCOMPtr.h" + + #include "nsIDOMEvent.h" #include "nsIDOMKeyListener.h" #include "nsIDOMMouseListener.h" @@ -26,8 +29,9 @@ #include "nsIDOMDragListener.h" #include "nsIDOMCompositionListener.h" #include "nsIDOMFocusListener.h" -#include "nsITextEditor.h" -#include "nsCOMPtr.h" + +#include "nsIEditor.h" +#include "nsIHTMLEditor.h" /** The nsTextEditorKeyListener public nsIDOMKeyListener * This class will delegate events to its editor according to the translation @@ -45,7 +49,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -63,7 +67,7 @@ protected: virtual nsresult ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProcessed); protected: - nsITextEditor* mEditor; // weak reference + nsIEditor* mEditor; // weak reference }; @@ -82,7 +86,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -94,11 +98,14 @@ public: /*END implementations of textevent handler interface*/ protected: - nsITextEditor* mEditor; // weak reference + nsIEditor* mEditor; // weak reference PRBool mCommitText; PRBool mInTransaction; }; + +class nsIEditorIMESupport; + class nsTextEditorCompositionListener : public nsIDOMCompositionListener { public: @@ -112,7 +119,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor); /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -125,7 +132,7 @@ public: /*END implementations of textevent handler interface*/ protected: - nsITextEditor* mEditor; // weak reference + nsIEditorIMESupport* mEditor; // weak reference }; @@ -144,7 +151,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -161,7 +168,7 @@ public: /*END implementations of mouseevent handler interface*/ protected: - nsITextEditor* mEditor; // weak reference + nsIEditor* mEditor; // weak reference }; @@ -181,7 +188,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -196,7 +203,7 @@ public: /*END implementations of dragevent handler interface*/ protected: - nsITextEditor* mEditor; + nsIEditor* mEditor; }; @@ -215,7 +222,7 @@ public: /** SetEditor gives an address to the editor that will be accessed * @param aEditor the editor this listener calls for editing operations */ - void SetEditor(nsITextEditor *aEditor){mEditor = aEditor;} + void SetEditor(nsIEditor *aEditor){mEditor = aEditor;} /*interfaces for addref and release and queryinterface*/ NS_DECL_ISUPPORTS @@ -228,34 +235,34 @@ public: /*END implementations of focus event handler interface*/ protected: - nsITextEditor* mEditor; // weak reference + nsIEditor* mEditor; // weak reference }; /** factory for the editor key listener */ -extern nsresult NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor mouse listener */ -extern nsresult NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor text listener */ -extern nsresult NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor drag listener */ -extern nsresult NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor composition listener */ -extern nsresult NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor *aEditor); /** factory for the editor composition listener */ -extern nsresult NS_NewEditorFocusListener(nsIDOMEventListener** aInstancePtrResult, nsITextEditor *aEditor); +extern nsresult NS_NewEditorFocusListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor *aEditor); #endif //editorInterfaces_h__ diff --git a/editor/libeditor/text/nsTextEditRules.cpp b/editor/libeditor/text/nsTextEditRules.cpp index d8b06bf0b94f..ff7f3b955949 100644 --- a/editor/libeditor/text/nsTextEditRules.cpp +++ b/editor/libeditor/text/nsTextEditRules.cpp @@ -17,10 +17,11 @@ */ #include "nsTextEditRules.h" -#include "nsTextEditor.h" + #include "nsEditor.h" #include "PlaceholderTxn.h" #include "InsertTextTxn.h" + #include "nsCOMPtr.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" @@ -37,7 +38,7 @@ static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \ - if (mFlags & TEXT_EDITOR_FLAG_READONLY || mFlags & TEXT_EDITOR_FLAG_DISABLED) \ + if ((mFlags & nsIHTMLEditor::eEditorReadonlyMask) || (mFlags & nsIHTMLEditor::eEditorDisabledMask)) \ { \ *aCancel = PR_TRUE; \ return NS_OK; \ @@ -47,10 +48,10 @@ static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); * Constructor/Destructor ********************************************************/ -nsTextEditRules::nsTextEditRules() +nsTextEditRules::nsTextEditRules(PRUint32 aFlags) +: mEditor(nsnull) +, mFlags(aFlags) { - mEditor = nsnull; - mFlags=0; } nsTextEditRules::~nsTextEditRules() @@ -64,10 +65,11 @@ nsTextEditRules::~nsTextEditRules() ********************************************************/ NS_IMETHODIMP -nsTextEditRules::Init(nsIEditor *aEditor) +nsTextEditRules::Init(nsHTMLEditor *aEditor) { if (!aEditor) { return NS_ERROR_NULL_POINTER; } - mEditor = (nsTextEditor*)aEditor; // we hold a non-refcounted reference back to our editor + + mEditor = aEditor; // we hold a non-refcounted reference back to our editor nsCOMPtr selection; mEditor->GetSelection(getter_AddRefs(selection)); NS_ASSERTION(selection, "editor cannot get selection"); @@ -92,19 +94,19 @@ nsTextEditRules::SetFlags(PRUint32 aFlags) // a style attribute on it, don't know why. // SetFlags() is really meant to only be called once // and at editor init time. - if (aFlags & TEXT_EDITOR_FLAG_PLAINTEXT) + if (aFlags & nsIHTMLEditor::eEditorPlaintextMask) { - if (!(mFlags & TEXT_EDITOR_FLAG_PLAINTEXT)) + if (!(mFlags & nsIHTMLEditor::eEditorPlaintextMask)) { // we are converting TO a plaintext editor // put a "white-space: pre" style on the body - nsCOMPtr bodyElement; - nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_SUCCEEDED(res) && bodyElement) - { - // not going through the editor to do this. - bodyElement->SetAttribute("style", "white-space: pre"); - } + nsCOMPtr bodyElement; + nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res) && bodyElement) + { + // not going through the editor to do this. + bodyElement->SetAttribute("style", "white-space: pre"); + } } } mFlags = aFlags; @@ -194,7 +196,9 @@ nsTextEditRules::DidDoAction(nsIDOMSelection *aSelection, nsresult nsTextEditRules::WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel) { - if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } + if (!aSelection || !aCancel) + return NS_ERROR_NULL_POINTER; + CANCEL_OPERATION_IF_READONLY_OR_DISABLED // initialize out param @@ -223,7 +227,7 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } CANCEL_OPERATION_IF_READONLY_OR_DISABLED - if (mFlags & TEXT_EDITOR_FLAG_SINGLELINE) { + if (mFlags & nsIHTMLEditor::eEditorSingleLineMask) { *aCancel = PR_TRUE; } else { @@ -256,7 +260,7 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, nsString inString = *aInString; // we might want to mutate the input // before setting the output, do that in a local var - if ((-1 != aMaxLength) && (mFlags&TEXT_EDITOR_FLAG_PLAINTEXT)) + if ((-1 != aMaxLength) && (mFlags & nsIHTMLEditor::eEditorPlaintextMask)) { // Get the current text length. // Get the length of inString. @@ -278,14 +282,14 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, if (selectionLength<0) { selectionLength *= (-1); } PRInt32 resultingDocLength = docLength - selectionLength; if (resultingDocLength >= aMaxLength) - { - *aOutString = ""; - *aCancel = PR_TRUE; - return result; - } - else - { - PRInt32 inCount = inString.Length(); + { + *aOutString = ""; + *aCancel = PR_TRUE; + return result; + } + else + { + PRInt32 inCount = inString.Length(); if ((inCount+resultingDocLength) > aMaxLength) { inString.Truncate(aMaxLength-resultingDocLength); @@ -298,7 +302,7 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, // initialize out params *aCancel = PR_FALSE; - if (mFlags&TEXT_EDITOR_FLAG_PASSWORD) + if (mFlags & nsIHTMLEditor::eEditorPasswordMask) { // manage the password buffer PRInt32 start, end; @@ -589,7 +593,7 @@ nsTextEditRules::InsertStyleAndNewTextNode(nsIDOMNode *aParentNode, nsIAtom *aTa result = mEditor->CreateNode(tag, aParentNode, 0, getter_AddRefs(newStyleNode)); if (NS_SUCCEEDED(result)) { - result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newStyleNode, 0, getter_AddRefs(newTextNode)); + result = mEditor->CreateNode(nsEditor::GetTextNodeTag(), newStyleNode, 0, getter_AddRefs(newTextNode)); if (NS_SUCCEEDED(result)) { if (aSelection) { @@ -606,7 +610,7 @@ nsTextEditRules::WillSetTextProperty(nsIDOMSelection *aSelection, PRBool *aCance nsresult result = NS_OK; // XXX: should probably return a success value other than NS_OK that means "not allowed" - if (TEXT_EDITOR_FLAG_PLAINTEXT & mFlags) { + if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) { *aCancel = PR_TRUE; } return result; @@ -624,7 +628,7 @@ nsTextEditRules::WillRemoveTextProperty(nsIDOMSelection *aSelection, PRBool *aCa nsresult result = NS_OK; // XXX: should probably return a success value other than NS_OK that means "not allowed" - if (TEXT_EDITOR_FLAG_PLAINTEXT & mFlags) { + if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) { *aCancel = PR_TRUE; } return result; @@ -638,7 +642,7 @@ nsTextEditRules::DidRemoveTextProperty(nsIDOMSelection *aSelection, nsresult aRe nsresult nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ECollapsedSelectionAction aCollapsedAction, + nsIEditor::ESelectionCollapseDirection aCollapsedAction, PRBool *aCancel) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } @@ -652,17 +656,17 @@ nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, *aCancel = PR_TRUE; return NS_OK; } - if (mFlags&TEXT_EDITOR_FLAG_PASSWORD) + if (mFlags & nsIHTMLEditor::eEditorPasswordMask) { // manage the password buffer PRInt32 start, end; mEditor->GetTextSelectionOffsets(aSelection, start, end); if (end==start) { // collapsed selection - if (nsIEditor::eDeleteLeft==aCollapsedAction && 0newTNode; - result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), mBogusNode, 0, + result = mEditor->CreateNode(nsEditor::GetTextNodeTag(), mBogusNode, 0, getter_AddRefs(newTNode)); if ((NS_SUCCEEDED(result)) && newTNode) { diff --git a/editor/libeditor/text/nsTextEditRules.h b/editor/libeditor/text/nsTextEditRules.h index 661b4d28276a..9b3eb8e75d76 100644 --- a/editor/libeditor/text/nsTextEditRules.h +++ b/editor/libeditor/text/nsTextEditRules.h @@ -19,10 +19,12 @@ #ifndef nsTextEditRules_h__ #define nsTextEditRules_h__ -#include "nsIEditor.h" -#include "nsEditRules.h" #include "nsCOMPtr.h" + +#include "nsHTMLEditor.h" #include "nsIDOMNode.h" + +#include "nsEditRules.h" #include "TypeInState.h" class PlaceholderTxn; @@ -43,11 +45,11 @@ class nsTextEditRules : public nsEditRules { public: - nsTextEditRules(); - virtual ~nsTextEditRules(); + nsTextEditRules(PRUint32 aFlags); + virtual ~nsTextEditRules(); // nsEditRules methods - NS_IMETHOD Init(nsIEditor *aEditor); + NS_IMETHOD Init(nsHTMLEditor *aEditor); NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel); NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); NS_IMETHOD GetFlags(PRUint32 *aFlags); @@ -96,10 +98,10 @@ protected: nsresult DidInsert(nsIDOMSelection *aSelection, nsresult aResult); nsresult WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ECollapsedSelectionAction aCollapsedAction, + nsIEditor::ESelectionCollapseDirection aCollapsedAction, PRBool *aCancel); nsresult DidDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ECollapsedSelectionAction aCollapsedAction, + nsIEditor::ESelectionCollapseDirection aCollapsedAction, nsresult aResult); nsresult WillSetTextProperty(nsIDOMSelection *aSelection, PRBool *aCancel); @@ -154,7 +156,7 @@ protected: nsresult PinSelectionInPRE(nsIDOMSelection *aSelection); // data - nsTextEditor *mEditor; // note that we do not refcount the editor + 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 PRUint32 mFlags; @@ -173,7 +175,7 @@ class nsTextRulesInfo : public nsRulesInfo outString(0), typeInState(), maxLength(-1), - collapsedAction(nsIEditor::eDeleteRight) + collapsedAction(nsIEditor::eDeleteNext) {}; virtual ~nsTextRulesInfo() {}; @@ -186,7 +188,7 @@ class nsTextRulesInfo : public nsRulesInfo PRInt32 maxLength; // kDeleteSelection - nsIEditor::ECollapsedSelectionAction collapsedAction; + nsIEditor::ESelectionCollapseDirection collapsedAction; // kMakeList PRBool bOrdered; diff --git a/editor/libeditor/txtsvc/nsTextServicesDocument.cpp b/editor/libeditor/txtsvc/nsTextServicesDocument.cpp index 8f095a138bb4..7b451c8a7e18 100644 --- a/editor/libeditor/txtsvc/nsTextServicesDocument.cpp +++ b/editor/libeditor/txtsvc/nsTextServicesDocument.cpp @@ -23,6 +23,7 @@ #include "nsIDOMNodeList.h" #include "nsIDOMRange.h" #include "nsIDOMSelection.h" +#include "nsIHighLevelHTMLEditor.h" #include "nsTextServicesDocument.h" #define LOCK_DOC(doc) @@ -1849,7 +1850,7 @@ nsTextServicesDocument::DeleteSelection() // Now delete the actual content! - result = mEditor->DeleteSelection(nsIEditor::eDeleteLeft); + result = mEditor->DeleteSelection(nsIEditor::eDeletePrevious); if (NS_FAILED(result)) { @@ -1953,7 +1954,9 @@ nsTextServicesDocument::InsertText(const nsString *aText) return result; } - result = mEditor->InsertText(*aText); + nsCOMPtr htmlEditor = do_QueryInterface(mEditor, &result); + if (htmlEditor) + result = htmlEditor->InsertText(*aText); if (NS_FAILED(result)) { diff --git a/editor/macbuild/editor.mcp b/editor/macbuild/editor.mcp index 9bb911dd51bb..74b52dc2c61d 100644 Binary files a/editor/macbuild/editor.mcp and b/editor/macbuild/editor.mcp differ diff --git a/editor/public/MANIFEST b/editor/public/MANIFEST index 8b1e28dc6919..1792f01eddd3 100644 --- a/editor/public/MANIFEST +++ b/editor/public/MANIFEST @@ -17,9 +17,13 @@ # nsIEditor.h -nsITextEditor.h nsEditorCID.h -nsIHTMLEditor.h -nsIEditRules.h +# nsIEditRules.h nsIEditActionListener.h nsICiter.h +nsIEditorIMESupport.h +nsIEditorLogging.h +nsIEditorMailSupport.h +nsIEditorStyleSheets.h +nsIHTMLEditor.h +nsITableEditor.h diff --git a/editor/public/Makefile.in b/editor/public/Makefile.in index 28a0f3eb8bd1..c2165d735af5 100644 --- a/editor/public/Makefile.in +++ b/editor/public/Makefile.in @@ -22,14 +22,17 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -EXPORTS = \ - nsIEditActionListener.h \ - nsIEditor.h \ - nsIHTMLEditor.h \ - nsITextEditor.h \ - nsIEditRules.h \ - nsEditorCID.h \ - nsICiter.h \ +EXPORTS = \ + nsIEditActionListener.h \ + nsIEditor.h \ + nsEditorCID.h \ + nsICiter.h \ + nsIEditorIMESupport.h \ + nsIEditorLogging.h \ + nsIEditorMailSupport.h \ + nsIEditorStyleSheets.h \ + nsIHTMLEditor.h \ + nsITableEditor.h \ $(NULL) EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS)) diff --git a/editor/public/makefile.win b/editor/public/makefile.win index aec6e6ece070..443f8cb1ecb6 100644 --- a/editor/public/makefile.win +++ b/editor/public/makefile.win @@ -18,14 +18,17 @@ DEPTH=..\.. IGNORE_MANIFEST=1 -EXPORTS = \ - nsIEditActionListener.h \ - nsIEditor.h \ - nsITextEditor.h \ - nsIHTMLEditor.h \ - nsIEditRules.h \ - nsEditorCID.h \ - nsICiter.h \ +EXPORTS = \ + nsIEditActionListener.h \ + nsIEditor.h \ + nsEditorCID.h \ + nsICiter.h \ + nsIEditorIMESupport.h \ + nsIEditorLogging.h \ + nsIEditorMailSupport.h \ + nsIEditorStyleSheets.h \ + nsIHTMLEditor.h \ + nsITableEditor.h \ $(NULL) MODULE = editor diff --git a/editor/public/nsEditorCID.h b/editor/public/nsEditorCID.h index 5cc73bf77bd8..27c78fcadb3c 100644 --- a/editor/public/nsEditorCID.h +++ b/editor/public/nsEditorCID.h @@ -5,11 +5,6 @@ 0xd3de3431, 0x8a75, 0x11d2, \ { 0x91, 0x8c, 0x0, 0x80, 0xc8, 0xe4, 0x4d, 0xb5 } } -#define NS_TEXTEDITOR_CID \ -{/* {bc37c640-c144-11d2-8f4c-006008159b0c}*/ \ -0xbc37c640, 0xc144, 0x11d2, \ -{ 0x8f, 0x4c, 0x0, 0x60, 0x08, 0x15, 0x9b, 0x0c } } - #define NS_HTMLEDITOR_CID \ {/* {ed0244e0-c144-11d2-8f4c-006008159b0c}*/ \ 0xed0244e0, 0xc144, 0x11d2, \ diff --git a/editor/public/nsIEditor.h b/editor/public/nsIEditor.h index dd4404dc45f5..e7e49986798c 100644 --- a/editor/public/nsIEditor.h +++ b/editor/public/nsIEditor.h @@ -21,65 +21,38 @@ #include "nsISupports.h" #include "nscore.h" -class nsIAtom; -class nsIDOMElement; -class nsIDOMNode; -class nsITransaction; -class nsIEditActionListener; -class nsIFileSpec; -class nsIPrivateTextRangeList; -class nsICSSStyleSheet; -class nsIOutputStream; -class nsIDocumentStateListener; -class nsIDOMDocument; -class nsIDOMSelection; -class nsIPresShell; -class nsString; -struct nsTextEventReply; -/* -Editor interface to outside world -*/ #define NS_IEDITOR_IID \ {/* A3C5EE71-742E-11d2-8F2C-006008310194*/ \ 0xa3c5ee71, 0x742e, 0x11d2, \ {0x8f, 0x2c, 0x0, 0x60, 0x8, 0x31, 0x1, 0x94} } -#define NS_IEDITORFACTORY_IID \ -{ /* {E2F4C7F1-864A-11d2-8F38-006008310194}*/ \ -0xe2f4c7f1, 0x864a, 0x11d2, \ -{ 0x8f, 0x38, 0x0, 0x60, 0x8, 0x31, 0x1, 0x94 } } -#define NS_ITEXTEDITORFACTORY_IID \ -{ /* {4a1f5ce0-c1f9-11d2-8f4c-006008159b0c}*/ \ -0x4a1f5ce0, 0xc1f9, 0x11d2, \ -{ 0x8f, 0x4c, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc } } +class nsString; -#define NS_IHTMLEDITORFACTORY_IID \ -{ /* BD62F312-CB8A-11d2-983A-00805F8AA8B8 */ \ -0xbd62f312, 0xcb8a, 0x11d2, \ -{ 0x98, 0x3a, 0x0, 0x80, 0x5f, 0x8a, 0xa8, 0xb8 } } +class nsIPresShell; +class nsIDOMNode; +class nsIDOMElement; +class nsIDOMDocument; +class nsIDOMSelection; +class nsITransaction; +class nsIOutputStream; +class nsIEditActionListener; +class nsIDocumentStateListener; - -/** - * A generic editor interface. - *

- * nsIEditor is the base interface used by applications to communicate with the editor. - * It makes no assumptions about the kind of content it is editing, - * other than the content being a DOM tree. - */ -class nsIEditor : public nsISupports{ +class nsIEditor : public nsISupports +{ public: + static const nsIID& GetIID() { static nsIID iid = NS_IEDITOR_IID; return iid; } + + /* An enum used to describe how to collpase a non-collapsed selection */ typedef enum { eDoNothing, - eDeleteRight, - eDeleteLeft - } ECollapsedSelectionAction; + eDeleteNext, + eDeletePrevious + } ESelectionCollapseDirection; - static nsString& GetTextNodeTag(); - - static const nsIID& GetIID() { static nsIID iid = NS_IEDITOR_IID; return iid; } /** * Init is to tell the implementation of nsIEditor to begin its services @@ -88,8 +61,9 @@ public: * once events can tell us from what pres shell they originated, * this will no longer be necessary and the editor will no longer be * linked to a single pres shell. + * @param aFlags A bitmask of flags for specifying the behavior of the editor. */ - NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell )=0; + NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, PRUint32 aFlags)=0; /** * PostCreate should be called after Init, and is the time that the editor tells @@ -97,6 +71,12 @@ public: */ NS_IMETHOD PostCreate()=0; + /** return the edit flags for this editor */ + NS_IMETHOD GetFlags(PRUint32 *aFlags)=0; + + /** set the edit flags for this editor. May be called at any time. */ + NS_IMETHOD SetFlags(PRUint32 aFlags)=0; + /** * return the DOM Document this editor is associated with * @@ -118,6 +98,137 @@ public: */ NS_IMETHOD GetSelection(nsIDOMSelection **aSelection)=0; + /* ------------ Selected content removal -------------- */ + + /** + * DeleteSelection removes all nodes in the current selection. + * @param aDir if eLTR, delete to the right (for example, the DEL key) + * if eRTL, delete to the left (for example, the BACKSPACE key) + */ + NS_IMETHOD DeleteSelection(ESelectionCollapseDirection aAction)=0; + + + /* ------------ Document info methods -------------- */ + + /** Respond to the menu 'Save' command; this may put up save UI if the document + * hasn't been saved yet. + */ + NS_IMETHOD Save()=0; + + /** Respond to the menu 'Save As' command; this will put up save UI + * @param aSavingCopy true if we are saving off a copy of the document + * without changing the disk file associated with the doc. + * This would correspond to a 'Save Copy As' menu command. + */ + NS_IMETHOD SaveAs(PRBool aSavingCopy)=0; + + /** Returns true if the document is modifed and needs saving */ + NS_IMETHOD GetDocumentModified(PRBool *outDocModified)=0; + + + + /* ------------ Transaction methods -------------- */ + + /** Do() fires a transaction. It is provided here so clients can create their own transactions. + * If a transaction manager is present, it is used. + * Otherwise, the transaction is just executed directly. + * + * @param aTxn the transaction to execute + */ + NS_IMETHOD Do(nsITransaction *aTxn)=0; + + + /** turn the undo system on or off + * @param aEnable if PR_TRUE, the undo system is turned on if it is available + * if PR_FALSE the undo system is turned off if it was previously on + * @return if aEnable is PR_TRUE, returns NS_OK if the undo system could be initialized properly + * if aEnable is PR_FALSE, returns NS_OK. + */ + NS_IMETHOD EnableUndo(PRBool aEnable)=0; + + /** Undo reverses the effects of the last Do operation, if Undo is enabled in the editor. + * It is provided here so clients need no knowledge of whether the editor has a transaction manager or not. + * If a transaction manager is present, it is told to undo and the result of + * that undo is returned. + * Otherwise, the Undo request is ignored and an error NS_ERROR_NOT_AVAILABLE is returned. + * + */ + NS_IMETHOD Undo(PRUint32 aCount)=0; + + /** returns state information about the undo system. + * @param aIsEnabled [OUT] PR_TRUE if undo is enabled + * @param aCanUndo [OUT] PR_TRUE if at least one transaction is currently ready to be undone. + */ + NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo)=0; + + /** Redo reverses the effects of the last Undo operation + * It is provided here so clients need no knowledge of whether the editor has a transaction manager or not. + * If a transaction manager is present, it is told to redo and the result of the previously undone + * transaction is reapplied to the document. + * If no transaction is available for Redo, or if the document has no transaction manager, + * the Redo request is ignored and an error NS_ERROR_NOT_AVAILABLE is returned. + * + */ + NS_IMETHOD Redo(PRUint32 aCount)=0; + + /** returns state information about the redo system. + * @param aIsEnabled [OUT] PR_TRUE if redo is enabled + * @param aCanRedo [OUT] PR_TRUE if at least one transaction is currently ready to be redone. + */ + NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo)=0; + + /** BeginTransaction is a signal from the caller to the editor that the caller will execute multiple updates + * to the content tree that should be treated as a single logical operation, + * in the most efficient way possible.
+ * All transactions executed between a call to BeginTransaction and EndTransaction will be undoable as + * an atomic action.
+ * EndTransaction must be called after BeginTransaction.
+ * Calls to BeginTransaction can be nested, as long as EndTransaction is called once per BeginUpdate. + */ + NS_IMETHOD BeginTransaction()=0; + + /** EndTransaction is a signal to the editor that the caller is finished updating the content model.
+ * BeginUpdate must be called before EndTransaction is called.
+ * Calls to BeginTransaction can be nested, as long as EndTransaction is called once per BeginTransaction. + */ + NS_IMETHOD EndTransaction()=0; + + + /* ------------ Clipboard methods -------------- */ + + /** cut the currently selected text, putting it into the OS clipboard + * What if no text is selected? + * What about mixed selections? + * What are the clipboard formats? + */ + NS_IMETHOD Cut()=0; + + /** copy the currently selected text, putting it into the OS clipboard + * What if no text is selected? + * What about mixed selections? + * What are the clipboard formats? + */ + NS_IMETHOD Copy()=0; + + /** paste the text in the OS clipboard at the cursor position, replacing + * the selected text (if any) + */ + NS_IMETHOD Paste()=0; + + /* ------------ Selection methods -------------- */ + + /** sets the document selection to the entire contents of the document */ + NS_IMETHOD SelectAll()=0; + + /** sets the document selection to the beginning of the document */ + NS_IMETHOD BeginningOfDocument()=0; + + /** sets the document selection to the end of the document */ + NS_IMETHOD EndOfDocument()=0; + + + /* ------------ Node manipulation methods -------------- */ + /** * SetAttribute() sets the attribute of aElement. * No checking is done to see if aAttribute is a legal attribute of the node, @@ -181,83 +292,6 @@ public: PRInt32 aPosition)=0; - /** - * InsertText() Inserts a string at the current location, given by the selection. - * If the selection is not collapsed, the selection is deleted and the insertion - * takes place at the resulting collapsed selection. - * - * NOTE: what happens if the string contains a CR? - * - * @param aString the string to be inserted - */ - NS_IMETHOD InsertText(const nsString& aStringToInsert)=0; - - /** - * BeginComposition() Handles the start of inline input composition. - */ - - NS_IMETHOD BeginComposition(void) = 0; - - /** - * SetCompositionString() Sets the inline input composition string. - * BeginComposition must be called prior to this. - */ - - NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList, nsTextEventReply* aReply) = 0; - - /** - * BeginComposition() Handles the end of inline input composition. - */ - - NS_IMETHOD EndComposition(void) = 0; - - /** - * Output methods flags: - */ - enum { - EditorOutputSelectionOnly = 1, - EditorOutputFormatted = 2, - EditorOutputNoDoctype = 4 - }; - - /** - * Output methods: - * aFormatType is a mime type, like text/plain. - */ - NS_IMETHOD OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags) = 0; - NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags) = 0; - /** - * And a debug method -- show us what the tree looks like right now - */ - NS_IMETHOD DumpContentTree() = 0; - - /** - * DeleteNode removes aChild from aParent. - * @param aChild The node to delete - */ - NS_IMETHOD DeleteNode(nsIDOMNode * aChild)=0; - - /** - * DeleteSelection removes all nodes in the current selection. - * @param aAction: direction to delete if selection is collapsed: - * if eDeleteRight, delete to the right (for example, the DEL key) - * if DeleteLeft, delete to the left (for example, the BACKSPACE key) - */ - NS_IMETHOD DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction)=0; - - /** - * DeleteSelectionAndCreateNode combines DeleteSelection and CreateNode - * It deletes only if there is something selected (doesn't do DEL, BACKSPACE action) - * @param aTag The type of object to create - * @param aNewNode [OUT] The node created. Caller must release aNewNode. - */ - NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode)=0; - /** * SplitNode() creates a new node identical to an existing node, and split the contents between the two nodes * @param aExistingRightNode the node to split. It will become the new node's next sibling. @@ -280,153 +314,40 @@ public: NS_IMETHOD JoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent)=0; + + + /** + * DeleteNode removes aChild from aParent. + * @param aChild The node to delete + */ + NS_IMETHOD DeleteNode(nsIDOMNode * aChild)=0; + + /* ------------ Output methods -------------- */ + + /** + * Output methods flags: + */ + enum { + EditorOutputSelectionOnly = 1, + EditorOutputFormatted = 2, + EditorOutputNoDoctype = 4 + }; /** - * Insert a break into the content model.
- * The interpretation of a break is up to the rule system: - * it may enter a character, split a node in the tree, etc. + * Output methods: + * aFormatType is a mime type, like text/plain. */ - NS_IMETHOD InsertBreak()=0; + NS_IMETHOD OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags) = 0; + NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags) = 0; - /** turn the undo system on or off - * @param aEnable if PR_TRUE, the undo system is turned on if it is available - * if PR_FALSE the undo system is turned off if it was previously on - * @return if aEnable is PR_TRUE, returns NS_OK if the undo system could be initialized properly - * if aEnable is PR_FALSE, returns NS_OK. - */ - NS_IMETHOD EnableUndo(PRBool aEnable)=0; - /** Do() fires a transaction. It is provided here so clients can create their own transactions. - * If a transaction manager is present, it is used. - * Otherwise, the transaction is just executed directly. - * - * @param aTxn the transaction to execute - */ - NS_IMETHOD Do(nsITransaction *aTxn)=0; - /** Undo reverses the effects of the last Do operation, if Undo is enabled in the editor. - * It is provided here so clients need no knowledge of whether the editor has a transaction manager or not. - * If a transaction manager is present, it is told to undo and the result of - * that undo is returned. - * Otherwise, the Undo request is ignored and an error NS_ERROR_NOT_AVAILABLE is returned. - * - */ - NS_IMETHOD Undo(PRUint32 aCount)=0; - - /** returns state information about the undo system. - * @param aIsEnabled [OUT] PR_TRUE if undo is enabled - * @param aCanUndo [OUT] PR_TRUE if at least one transaction is currently ready to be undone. - */ - NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo)=0; - - /** Redo reverses the effects of the last Undo operation - * It is provided here so clients need no knowledge of whether the editor has a transaction manager or not. - * If a transaction manager is present, it is told to redo and the result of the previously undone - * transaction is reapplied to the document. - * If no transaction is available for Redo, or if the document has no transaction manager, - * the Redo request is ignored and an error NS_ERROR_NOT_AVAILABLE is returned. - * - */ - NS_IMETHOD Redo(PRUint32 aCount)=0; - - /** returns state information about the redo system. - * @param aIsEnabled [OUT] PR_TRUE if redo is enabled - * @param aCanRedo [OUT] PR_TRUE if at least one transaction is currently ready to be redone. - */ - NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo)=0; - - /** BeginTransaction is a signal from the caller to the editor that the caller will execute multiple updates - * to the content tree that should be treated as a single logical operation, - * in the most efficient way possible.
- * All transactions executed between a call to BeginTransaction and EndTransaction will be undoable as - * an atomic action.
- * EndTransaction must be called after BeginTransaction.
- * Calls to BeginTransaction can be nested, as long as EndTransaction is called once per BeginUpdate. - */ - NS_IMETHOD BeginTransaction()=0; - - /** EndTransaction is a signal to the editor that the caller is finished updating the content model.
- * BeginUpdate must be called before EndTransaction is called.
- * Calls to BeginTransaction can be nested, as long as EndTransaction is called once per BeginTransaction. - */ - NS_IMETHOD EndTransaction()=0; - - /** Get the layout's frame object associated with the node - * Needed when we must get geometric layout info, such as what column a table cell is in. - * @param aNode The DOM node we need the frame for - * @param aLayoutObject The pointer where the resulting object is placed - */ - NS_IMETHOD GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject)=0; - - /** scroll the viewport so the selection is in view. - * @param aScrollToBegin PR_TRUE if the beginning of the selection is to be scrolled into view. - * PR_FALSE if the end of the selection is to be scrolled into view - */ - NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin)=0; - - /** sets the document selection to the entire contents of the document */ - NS_IMETHOD SelectAll()=0; - - /** sets the document selection to the beginning of the document */ - NS_IMETHOD BeginningOfDocument()=0; - - /** sets the document selection to the end of the document */ - NS_IMETHOD EndOfDocument()=0; - - /** cut the currently selected text, putting it into the OS clipboard - * What if no text is selected? - * What about mixed selections? - * What are the clipboard formats? - */ - NS_IMETHOD Cut()=0; - - /** copy the currently selected text, putting it into the OS clipboard - * What if no text is selected? - * What about mixed selections? - * What are the clipboard formats? - */ - NS_IMETHOD Copy()=0; - - /** paste the text in the OS clipboard at the cursor position, replacing - * the selected text (if any) - */ - NS_IMETHOD Paste()=0; - - /** paste the text in the OS clipboard at the cursor position, - * replacing the selected text (if any). - * Method of quotation will depend on what type of editor we are, - * and also on preference settings. - * @param aCitation The "mid" URL of the source message - */ - NS_IMETHOD PasteAsQuotation()=0; - - /** insert a string as quoted text, - * replacing the selected text (if any). - * Method of quotation will depend on what type of editor we are, - * and also on preference settings. - * @param aQuotedText The actual text to be quoted - * @param aCitation The "mid" URL of the source message - */ - NS_IMETHOD InsertAsQuotation(const nsString& aQuotedText)=0; - - /** load and apply the style sheet, specified by aURL, to - * the editor's document. This can involve asynchronous - * network I/O - * @param aURL The style sheet to be loaded and applied. - */ - NS_IMETHOD ApplyStyleSheet(const nsString& aURL)=0; - - /** Add the given Style Sheet to the editor's document - * This is always synchronous - * @param aSheet The style sheet to be applied. - */ - NS_IMETHOD AddStyleSheet(nsICSSStyleSheet* aSheet)=0; - - /** Remove the given Style Sheet from the editor's document - * This is always synchronous - * @param aSheet The style sheet to be applied. - */ - NS_IMETHOD RemoveStyleSheet(nsICSSStyleSheet* aSheet)=0; + /* ------------ Various listeners methods -------------- */ /** add an EditActionListener to the editors list of listeners. */ NS_IMETHOD AddEditActionListener(nsIEditActionListener *aListener)=0; @@ -439,22 +360,24 @@ public: /** Remove a DocumentStateListener to the editors list of doc state listeners. */ NS_IMETHOD RemoveDocumentStateListener(nsIDocumentStateListener *aListener)=0; - - /** Returns true if the document is modifed and needs saving */ - NS_IMETHOD GetDocumentModified(PRBool *outDocModified)=0; - + + + /* ------------ Debug methods -------------- */ + + /** + * And a debug method -- show us what the tree looks like right now + */ + NS_IMETHOD DumpContentTree() = 0; + /** Dumps a text representation of the content tree to standard out */ NS_IMETHOD DebugDumpContent() const =0; /* Run unit tests. Noop in optimized builds */ NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)=0; - /* Start logging */ - NS_IMETHOD StartLogging(nsIFileSpec *aLogFile)=0; - /* Stop logging */ - NS_IMETHOD StopLogging()=0; }; + #endif //nsIEditor_h__ diff --git a/editor/public/nsIHTMLEditor.h b/editor/public/nsIHTMLEditor.h index faf10054cade..0b4c13b2e2b2 100644 --- a/editor/public/nsIHTMLEditor.h +++ b/editor/public/nsIHTMLEditor.h @@ -16,126 +16,197 @@ * Reserved. */ + #ifndef nsIHTMLEditor_h__ #define nsIHTMLEditor_h__ -#define NS_IHTMLEDITOR_IID \ -{/*BD62F311-CB8A-11d2-983A-00805F8AA8B8*/ \ -0xbd62f311, 0xcb8a, 0x11d2, \ -{ 0x98, 0x3a, 0x0, 0x80, 0x5f, 0x8a, 0xa8, 0xb8 } } + +#define NS_IHTMLEDITOR_IID \ +{ /* 4805e683-49b9-11d3-9ce4-ed60bd6cb5bc} */ \ +0x4805e683, 0x49b9, 0x11d3, \ +{ 0x9c, 0xe4, 0xed, 0x60, 0xbd, 0x6c, 0xb5, 0xbc } } -#include "nsIEditor.h" -#include "nscore.h" -//#include "nsIDOMDocumentFragment.h" - -class nsIEditorCallback; -class nsISupportsArray; +class nsString; class nsStringArray; -class nsIAtom; -class nsIOutputStream; -class nsIDOMWindow; -class nsIFileSpec; -struct nsTextEventReply; -/** - * The HTML editor interface. - *

- * Use to edit text and other HTML objects represented as a DOM tree. - */ -class nsIHTMLEditor : public nsISupports{ +class nsIAtom; +class nsIDOMNode; +class nsIDOMElement; + + +class nsIHTMLEditor : public nsISupports +{ public: static const nsIID& GetIID() { static nsIID iid = NS_IHTMLEDITOR_IID; return iid; } - typedef enum {eSaveFileText = 0, eSaveFileHTML = 1 } ESaveFileType; - /** Initialize the HTML editor - * - */ - NS_IMETHOD Init(nsIDOMDocument *aDoc, - nsIPresShell *aPresShell)=0; + // the bits in an editor behavior mask. + enum { + eEditorPlaintextBit = 0, // only plain text entry is allowed via events + eEditorSingleLineBit, // enter key and CR-LF handled specially + eEditorPasswordBit, // text is not entered into content, only a representative character + eEditorReadonlyBit, // editing events are disabled. Editor may still accept focus. + eEditorDisabledBit, // all events are disabled (like scrolling). Editor will not accept focus. + eEditorFilterInputBit // text input is limited to certain character types, use mFilter + + // max 32 bits + }; + + enum { + eEditorPlaintextMask = (1 << eEditorPlaintextBit), + eEditorSingleLineMask = (1 << eEditorSingleLineBit), + eEditorPasswordMask = (1 << eEditorPasswordBit), + eEditorReadonlyMask = (1 << eEditorReadonlyBit), + eEditorDisabledMask = (1 << eEditorDisabledBit), + eEditorFilterInputMask = (1 << eEditorFilterInputBit) + }; + + + /* ------------ Document info methods -------------- */ -// Methods shared with nsITextEditor (see nsITextEditor.h for details) - NS_IMETHOD SetTextProperty(nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue)=0; - NS_IMETHOD GetTextProperty(nsIAtom *aProperty, - const nsString *aAttribute, - const nsString *aValue, - PRBool &aFirst, PRBool &aAll, PRBool &aAny)=0; - NS_IMETHOD GetParagraphFormat(nsString& aParagraphFormat)=0; - NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat)=0; - NS_IMETHOD RemoveTextProperty(nsIAtom *aProperty, const nsString *aAttribute)=0; - NS_IMETHOD DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction)=0; - NS_IMETHOD InsertText(const nsString& aStringToInsert)=0; - NS_IMETHOD InsertBreak()=0; - NS_IMETHOD EnableUndo(PRBool aEnable)=0; - NS_IMETHOD Undo(PRUint32 aCount)=0; - NS_IMETHOD CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo)=0; - NS_IMETHOD Redo(PRUint32 aCount)=0; - NS_IMETHOD CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo)=0; - NS_IMETHOD BeginTransaction()=0; - NS_IMETHOD EndTransaction()=0; - NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection)=0; - NS_IMETHOD MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection)=0; - NS_IMETHOD MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection)=0; - NS_IMETHOD MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection)=0; - NS_IMETHOD SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection)=0; - NS_IMETHOD SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection)=0; - NS_IMETHOD SelectAll()=0; - NS_IMETHOD BeginningOfDocument()=0; - NS_IMETHOD EndOfDocument()=0; - NS_IMETHOD ScrollUp(nsIAtom *aIncrement)=0; - NS_IMETHOD ScrollDown(nsIAtom *aIncrement)=0; - NS_IMETHOD ScrollIntoView(PRBool aScrollToBegin)=0; + /** get the length of the document in characters */ + NS_IMETHOD GetDocumentLength(PRInt32 *aCount)=0; - NS_IMETHOD Save()=0; - NS_IMETHOD SaveAs(PRBool aSavingCopy)=0; + NS_IMETHOD SetMaxTextLength(PRInt32 aMaxTextLength)=0; + NS_IMETHOD GetMaxTextLength(PRInt32& aMaxTextLength)=0; - NS_IMETHOD Cut()=0; - NS_IMETHOD Copy()=0; - NS_IMETHOD Paste()=0; - NS_IMETHOD PasteAsQuotation()=0; - NS_IMETHOD PasteAsCitedQuotation(const nsString& aCitation)=0; - NS_IMETHOD InsertAsQuotation(const nsString& aQuotedText)=0; - NS_IMETHOD InsertAsCitedQuotation(const nsString& aQuotedText, const nsString& aCitation)=0; + /* ------------ Inline property methods -------------- */ - NS_IMETHOD InsertHTML(const nsString &aInputString)=0; /** - * Output methods: - * aFormatType is a mime type, like text/plain. + * SetInlineProperty() sets the aggregate properties on the current selection + * + * @param aProperty the property to set on the selection + * @param aAttribute the attribute of the property, if applicable. May be null. + * Example: aProperty="font", aAttribute="color" + * @param aValue if aAttribute is not null, the value of the attribute. May be null. + * Example: aProperty="font", aAttribute="color", aValue="0x00FFFF" */ - NS_IMETHOD OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags) = 0; - NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags) = 0; + NS_IMETHOD SetInlineProperty(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue)=0; - NS_IMETHOD ApplyStyleSheet(const nsString& aURL)=0; - -// Miscellaneous Methods - /** Set the background color of the selected table cell, row, columne, or table, - * or the document background if not in a table + /** + * GetInlineProperty() gets the aggregate properties of the current selection. + * All object in the current selection are scanned and their attributes are + * represented in a list of Property object. + * + * @param aProperty the property to get on the selection + * @param aAttribute the attribute of the property, if applicable. May be null. + * Example: aProperty="font", aAttribute="color" + * @param aValue if aAttribute is not null, the value of the attribute. May be null. + * Example: aProperty="font", aAttribute="color", aValue="0x00FFFF" + * @param aFirst [OUT] PR_TRUE if the first text node in the selection has the property + * @param aAny [OUT] PR_TRUE if any of the text nodes in the selection have the property + * @param aAll [OUT] PR_TRUE if all of the text nodes in the selection have the property */ - NS_IMETHOD SetBackgroundColor(const nsString& aColor)=0; - /** Set any BODY element attribute - */ - NS_IMETHOD SetBodyAttribute(const nsString& aAttr, const nsString& aValue)=0; + NS_IMETHOD GetInlineProperty(nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue, + PRBool &aFirst, PRBool &aAny, PRBool &aAll)=0; - /* - NS_IMETHOD CheckSpelling()=0; - NS_IMETHOD SpellingLanguage(nsIAtom *aLanguage)=0; - */ - /* The editor doesn't know anything about specific services like SpellChecking. - * Services can be invoked on the content, and these services can use the editor if they choose - * to get transactioning/undo/redo. - * For things like auto-spellcheck, the App should implement nsIDocumentObserver and - * trigger off of ContentChanged notifications. + /** + * RemoveInlineProperty() deletes the properties from all text in the current selection. + * If aProperty is not set on the selection, nothing is done. + * + * @param aProperty the property to reomve from the selection + * @param aAttribute the attribute of the property, if applicable. May be null. + * Example: aProperty="font", aAttribute="color" + * nsIEditProperty::allAttributes is special. It indicates that + * all content-based text properties are to be removed from the selection. */ + NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute)=0; + + /* ------------ HTML content methods -------------- */ + + /** + * Insert a break into the content model.
+ * The interpretation of a break is up to the rule system: + * it may enter a character, split a node in the tree, etc. + */ + NS_IMETHOD InsertBreak()=0; + + /** + * InsertText() Inserts a string at the current location, given by the selection. + * If the selection is not collapsed, the selection is deleted and the insertion + * takes place at the resulting collapsed selection. + * + * NOTE: what happens if the string contains a CR? + * + * @param aString the string to be inserted + */ + NS_IMETHOD InsertText(const nsString& aStringToInsert)=0; + + /** + * document me! + */ + NS_IMETHOD InsertHTML(const nsString &aInputString)=0; + + + /** Insert an element, which may have child nodes, at the selection + * Used primarily to insert a new element for various insert element dialogs, + * but it enforces the HTML 4.0 DTD "CanContain" rules, so it should + * be useful for other elements. + * + * @param aElement The element to insert + * @param aDeleteSelection Delete the selection before inserting + * If aDeleteSelection is PR_FALSE, then the element is inserted + * after the end of the selection for all element except + * Named Anchors, which insert before the selection + */ + NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection)=0; + + + + /** + * DeleteSelectionAndCreateNode combines DeleteSelection and CreateNode + * It deletes only if there is something selected (doesn't do DEL, BACKSPACE action) + * @param aTag The type of object to create + * @param aNewNode [OUT] The node created. Caller must release aNewNode. + */ + NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode)=0; + + /* ------------ Selection manipulation -------------- */ + /* Should these be moved to nsIDOMSelection? */ + + /** Set the selection at the suppled element + * + * @param aElement An element in the document + */ + NS_IMETHOD SelectElement(nsIDOMElement* aElement)=0; + + /** Create a collapsed selection just after aElement + * + * XXX could we parameterize SelectElement(before/select/after>? + * + * The selection is set to parent-of-aElement with an + * offset 1 greater than aElement's offset + * but it enforces the HTML 4.0 DTD "CanContain" rules, so it should + * be useful for other elements. + * + * @param aElement An element in the document + */ + NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement)=0; + + + /** + * Document me! + * + */ + NS_IMETHOD GetParagraphFormat(nsString& aParagraphFormat)=0; + + /** + * Document me! + * + */ + NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat)=0; + + /** + * Document me! + * + */ NS_IMETHOD GetParagraphStyle(nsStringArray *aTagList)=0; /** Add a block parent node around the selected content. @@ -167,8 +238,22 @@ public: */ NS_IMETHOD RemoveParent(const nsString &aParentTag)=0; + /** + * Document me! + * + */ NS_IMETHOD InsertList(const nsString& aListType)=0; + + /** + * Document me! + * + */ NS_IMETHOD Indent(const nsString& aIndent)=0; + + /** + * Document me! + * + */ NS_IMETHOD Align(const nsString& aAlign)=0; /** Return the input node or a parent matching the given aTagName, @@ -205,6 +290,9 @@ public: NS_IMETHOD GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn)=0; /** Return a new element with default attribute values + * + * This does not rely on the selection, and is not sensitive to context. + * * Used primarily to supply new element for various insert element dialogs * (Image, Link, NamedAnchor, Table, and HorizontalRule * are the only returned elements as of 7/25/99) @@ -217,23 +305,12 @@ public: * (an "A" tag with the "name" attribute set) */ NS_IMETHOD CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement** aReturn)=0; - - /** Insert an element, which may have child nodes, at the selection - * Used primarily to insert a new element for various insert element dialogs, - * but it enforces the HTML 4.0 DTD "CanContain" rules, so it should - * be useful for other elements. - * - * @param aElement The element to insert - * @param aDeleteSelection Delete the selection before inserting - * If aDeleteSelection is PR_FALSE, then the element is inserted - * after the end of the selection for all element except - * Named Anchors, which insert before the selection - */ - NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection)=0; /** Save the attributes of a Horizontal Rule in user preferences * These prefs are used when the user inserts a new Horizontal line * + * XXX this functionality should move to the editorShell. + * * @param aElement An HR element */ NS_IMETHOD SaveHLineSettings(nsIDOMElement* aElement)=0; @@ -244,133 +321,20 @@ public: * @param aElement An "A" element with a non-empty "href" attribute */ NS_IMETHOD InsertLinkAroundSelection(nsIDOMElement* aAnchorElement)=0; - - /** Set the selection at the suppled element - * - * @param aElement An element in the document - */ - NS_IMETHOD SelectElement(nsIDOMElement* aElement)=0; - /** Create a collapsed selection just after aElement - * The selection is set to parent-of-aElement with an - * offset 1 greater than aElement's offset - * but it enforces the HTML 4.0 DTD "CanContain" rules, so it should - * be useful for other elements. - * - * @param aElement An element in the document - */ - NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement)=0; - -// MHTML helper methods - NS_IMETHOD GetEmbeddedObjects(nsISupportsArray** aNodeList)=0; - -// Table editing Methods - /** Insert table methods - * Insert relative to the selected cell or the - * cell enclosing the selection anchor - * The selection is collapsed and is left in the new cell - * at the same row,col location as the original anchor cell - * - * @param aNumber Number of items to insert - * @param aAfter If TRUE, insert after the current cell, - * else insert before current cell - */ - NS_IMETHOD InsertTableCell(PRInt32 aNumber, PRBool aAfter)=0; - NS_IMETHOD InsertTableColumn(PRInt32 aNumber, PRBool aAfter)=0; - NS_IMETHOD InsertTableRow(PRInt32 aNumber, PRBool aAfter)=0; - - /** Delete table methods - * Delete starting at the selected cell or the - * cell (or table) enclosing the selection anchor - * The selection is collapsed and is left in the - * cell at the same row,col location as - * the previous selection anchor, if possible, - * else in the closest neigboring cell - * - * @param aNumber Number of items to insert/delete - */ - NS_IMETHOD DeleteTable()=0; - NS_IMETHOD DeleteTableCell(PRInt32 aNumber)=0; - NS_IMETHOD DeleteTableColumn(PRInt32 aNumber)=0; - NS_IMETHOD DeleteTableRow(PRInt32 aNumber)=0; - - /** Join the contents of the selected cells into one cell, - * expanding that cells ROWSPAN and COLSPAN to take up - * the same number of cellmap locations as before. - * Cells whose contents were moved are deleted. - * If there's one cell selected or caret is in one cell, - * it is joined with the cell to the right, if it exists - */ - NS_IMETHOD JoinTableCells()=0; - - /** Scan through all rows and add cells as needed so - * all locations in the cellmap are occupied. - * Used after inserting single cells or pasting - * a collection of cells that extend past the - * previous size of the table - * If aTable is null, it uses table enclosing the selection anchor - */ - NS_IMETHOD NormalizeTable(nsIDOMElement *aTable)=0; - - /** Get the row an column index from the layout's cellmap - * If aTable is null, it will try to find enclosing table of selection ancho - * - */ - NS_IMETHOD GetCellIndexes(nsIDOMElement *aCell, PRInt32& aRowIndex, PRInt32& aColIndex)=0; - - /** Get the number of rows and columns in a table from the layout's cellmap - * If aTable is null, it will try to find enclosing table of selection ancho - * Note that all rows in table will not have this many because of - * ROWSPAN effects or if table is not "rectangular" (has short rows) - */ - NS_IMETHOD GetTableSize(nsIDOMElement *aTable, PRInt32& aRowCount, PRInt32& aColCount)=0; - - /** Get a cell element at cellmap grid coordinates - * A cell that spans across multiple cellmap locations will - * be returned multiple times, once for each location it occupies - * - * @param aTable A table in the document - * @param aRowIndex, aColIndex The 0-based cellmap indexes - * - * Note that this returns NS_TABLELAYOUT_CELL_NOT_FOUND - * when a cell is not found at the given indexes, - * but this passes the NS_SUCCEEDED() test, - * so you can scan for all cells in a row or column - * by iterating through the appropriate indexes - * until the returned aCell is null - */ - NS_IMETHOD GetCellAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell)=0; - - /** Get a cell at cellmap grid coordinates and associated data - * A cell that spans across multiple cellmap locations will - * be returned multiple times, once for each location it occupies - * Examine the returned aStartRowIndex and aStartColIndex to see - * if it is in the same layout column or layout row: - * A "layout row" is all cells sharing the same top edge - * A "layout column" is all cells sharing the same left edge - * This is important to determine what to do when inserting or deleting a column or row - * - * @param aTable A table in the document - * @param aRowIndex, aColIndex The 0-based cellmap indexes - * - * Note that this returns NS_TABLELAYOUT_CELL_NOT_FOUND - * when a cell is not found at the given indexes (see note for GetCellAt()) - */ - NS_IMETHOD GetCellDataAt(nsIDOMElement* aTable, PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell, - PRInt32& aStartRowIndex, PRInt32& aStartColIndex, - PRInt32& aRowSpan, PRInt32& aColSpan, PRBool& aIsSelected)=0; + /** + * Document me! + * + */ + NS_IMETHOD SetBackgroundColor(const nsString& aColor)=0; -// IME editing Methods - NS_IMETHOD BeginComposition(void)=0; - NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply *aReply)=0; - NS_IMETHOD EndComposition(void)=0; + /** + * Document me! + * + */ + NS_IMETHOD SetBodyAttribute(const nsString& aAttr, const nsString& aValue)=0; - - // Logging Methods - NS_IMETHOD StartLogging(nsIFileSpec *aLogFile)=0; - NS_IMETHOD StopLogging()=0; }; -#endif //nsIEditor_h__ - +#endif // nsIHTMLEditor_h__ diff --git a/editor/txtsvc/src/nsTextServicesDocument.cpp b/editor/txtsvc/src/nsTextServicesDocument.cpp index 8f095a138bb4..7b451c8a7e18 100644 --- a/editor/txtsvc/src/nsTextServicesDocument.cpp +++ b/editor/txtsvc/src/nsTextServicesDocument.cpp @@ -23,6 +23,7 @@ #include "nsIDOMNodeList.h" #include "nsIDOMRange.h" #include "nsIDOMSelection.h" +#include "nsIHighLevelHTMLEditor.h" #include "nsTextServicesDocument.h" #define LOCK_DOC(doc) @@ -1849,7 +1850,7 @@ nsTextServicesDocument::DeleteSelection() // Now delete the actual content! - result = mEditor->DeleteSelection(nsIEditor::eDeleteLeft); + result = mEditor->DeleteSelection(nsIEditor::eDeletePrevious); if (NS_FAILED(result)) { @@ -1953,7 +1954,9 @@ nsTextServicesDocument::InsertText(const nsString *aText) return result; } - result = mEditor->InsertText(*aText); + nsCOMPtr htmlEditor = do_QueryInterface(mEditor, &result); + if (htmlEditor) + result = htmlEditor->InsertText(*aText); if (NS_FAILED(result)) {