diff --git a/editor/base/DeleteRangeTxn.cpp b/editor/base/DeleteRangeTxn.cpp index ee00372d2e42..063cc1448b42 100644 --- a/editor/base/DeleteRangeTxn.cpp +++ b/editor/base/DeleteRangeTxn.cpp @@ -167,7 +167,8 @@ NS_IMETHODIMP DeleteRangeTxn::Undo(void) nsCOMPtr selection; result = mEditor->GetSelection(getter_AddRefs(selection)); if (NS_SUCCEEDED(result)) { - result = selection->Collapse(mStartParent, mStartOffset); + selection->Collapse(mStartParent, mStartOffset); + selection->Extend(mEndParent, mEndOffset); } } diff --git a/editor/base/EditAggregateTxn.cpp b/editor/base/EditAggregateTxn.cpp index 4fc8b1c8f558..e6a93f29632f 100644 --- a/editor/base/EditAggregateTxn.cpp +++ b/editor/base/EditAggregateTxn.cpp @@ -153,26 +153,6 @@ NS_IMETHODIMP EditAggregateTxn::AppendChild(EditTxn *aTxn) return NS_ERROR_NULL_POINTER; } -NS_IMETHODIMP EditAggregateTxn::SetName(nsIAtom *aName) -{ - mName = do_QueryInterface(aName); - return NS_OK; -} - -NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName) -{ - if (aName) - { - if (mName) - { - *aName = mName; - NS_ADDREF(*aName); - return NS_OK; - } - } - return NS_ERROR_NULL_POINTER; -} - NS_IMETHODIMP EditAggregateTxn::GetCount(PRInt32 *aCount) { if (!aCount) { @@ -205,6 +185,28 @@ NS_IMETHODIMP EditAggregateTxn::GetTxnAt(PRInt32 aIndex, EditTxn **aTxn) } +NS_IMETHODIMP EditAggregateTxn::SetName(nsIAtom *aName) +{ + mName = do_QueryInterface(aName); + return NS_OK; +} + +NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName) +{ + if (aName) + { + if (mName) + { + *aName = mName; + NS_ADDREF(*aName); + return NS_OK; + } + } + return NS_ERROR_NULL_POINTER; +} + + + diff --git a/editor/base/EditAggregateTxn.h b/editor/base/EditAggregateTxn.h index 1ad6a5442c75..2e83a62e4a5b 100644 --- a/editor/base/EditAggregateTxn.h +++ b/editor/base/EditAggregateTxn.h @@ -71,10 +71,10 @@ public: */ NS_IMETHOD GetTxnAt(PRInt32 aIndex, EditTxn **aTxn); - /** set the name assigned to this aggregate txn */ + /** set the name assigned to this txn */ NS_IMETHOD SetName(nsIAtom *aName); - /** get the name assigned to this aggregate txn */ + /** get the name assigned to this txn */ NS_IMETHOD GetName(nsIAtom **aName); protected: @@ -82,7 +82,6 @@ protected: //XXX: if this was an nsISupportsArray, it would handle refcounting for us nsVoidArray * mChildren; nsCOMPtr mName; - }; #endif diff --git a/editor/base/EditTxn.h b/editor/base/EditTxn.h index 4323ef56bc57..60fbb57b862a 100644 --- a/editor/base/EditTxn.h +++ b/editor/base/EditTxn.h @@ -20,8 +20,7 @@ #define EditTxn_h__ #include "nsITransaction.h" - -class nsIDOMNode; +#include "nsCOMPtr.h" #define EDIT_TXN_IID \ {/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \ @@ -31,6 +30,8 @@ class nsIDOMNode; /** * base class for all document editing transactions. * provides default concrete behavior for all nsITransaction methods. + * EditTxns optionally have a name. This name is for internal purposes only, + * it is never seen by the user or by any external entity. */ class EditTxn : public nsITransaction { diff --git a/editor/base/InsertTextTxn.cpp b/editor/base/InsertTextTxn.cpp index b7b718ae0996..70c86cfc48a1 100644 --- a/editor/base/InsertTextTxn.cpp +++ b/editor/base/InsertTextTxn.cpp @@ -119,19 +119,23 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti otherTxn->GetName(getter_AddRefs(txnName)); if (txnName.get()==gInsertTextTxnName) { // yep, it's one of ours. By definition, it must contain only - // a single InsertTextTxn + // another aggregate with a single child, + // or a single InsertTextTxn nsCOMPtr childTxn; otherTxn->GetTxnAt(0, getter_AddRefs(childTxn)); - nsCOMPtr otherInsertTxn; - otherInsertTxn = do_QueryInterface(childTxn, &result); - if (otherInsertTxn) + if (childTxn) { - if (PR_TRUE==IsSequentialInsert(otherInsertTxn)) + nsCOMPtr otherInsertTxn; + otherInsertTxn = do_QueryInterface(childTxn, &result); + if (otherInsertTxn) { - nsAutoString otherData; - otherInsertTxn->GetData(otherData); - mStringToInsert += otherData; - *aDidMerge = PR_TRUE; + if (PR_TRUE==IsSequentialInsert(otherInsertTxn)) + { + nsAutoString otherData; + otherInsertTxn->GetData(otherData); + mStringToInsert += otherData; + *aDidMerge = PR_TRUE; + } } } } diff --git a/editor/base/Makefile.in b/editor/base/Makefile.in index c666f3e87762..f5e2ccb800d0 100644 --- a/editor/base/Makefile.in +++ b/editor/base/Makefile.in @@ -49,6 +49,7 @@ CPPSRCS = \ DeleteTableRowTxn.cpp \ JoinTableCellsTxn.cpp \ InsertTextTxn.cpp \ + PlaceholderTxn.cpp \ DeleteTextTxn.cpp \ CreateElementTxn.cpp \ InsertElementTxn.cpp \ diff --git a/editor/base/PlaceholderTxn.cpp b/editor/base/PlaceholderTxn.cpp new file mode 100644 index 000000000000..941d6e05c955 --- /dev/null +++ b/editor/base/PlaceholderTxn.cpp @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "PlaceholderTxn.h" +#include "nsVoidArray.h" + +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + + +PlaceholderTxn::PlaceholderTxn() + : EditAggregateTxn() +{ + mAbsorb=PR_TRUE; +} + + +PlaceholderTxn::~PlaceholderTxn() +{ +} + +NS_IMETHODIMP PlaceholderTxn::Do(void) +{ + return NS_OK; +} + +NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) +{ + // set out param default value + if (nsnull!=aDidMerge) + *aDidMerge=PR_FALSE; + nsresult result = NS_OK; + if ((nsnull!=aDidMerge) && (nsnull!=aTransaction)) + { + EditTxn *editTxn = (EditTxn*)aTransaction; //XXX: hack, not safe! need nsIEditTransaction! + if (PR_TRUE==mAbsorb) + { // yep, it's one of ours. Assimilate it. + AppendChild(editTxn); + *aDidMerge = PR_TRUE; + } + else + { // let our last child txn make the choice + PRInt32 count = mChildren->Count(); + if (0ElementAt(count-1)); + if (lastTxn) + { + lastTxn->Merge(aDidMerge, aTransaction); + } + } + } + } + return result; +} + diff --git a/editor/base/PlaceholderTxn.h b/editor/base/PlaceholderTxn.h new file mode 100644 index 000000000000..6f6f39953a39 --- /dev/null +++ b/editor/base/PlaceholderTxn.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef AggregatePlaceholderTxn_h__ +#define AggregatePlaceholderTxn_h__ + +#include "EditAggregateTxn.h" + +#define PLACEHOLDER_TXN_IID \ +{/* {0CE9FB00-D9D1-11d2-86DE-000064657374} */ \ +0x0CE9FB00, 0xD9D1, 0x11d2, \ +{0x86, 0xde, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + +/** + * An aggregate transaction that knows how to absorb all subsequent + * transactions with the same name. This transaction does not "Do" anything. + * But it absorbs other transactions via merge, and can undo/redo the + * transactions it has absorbed. + */ +class PlaceholderTxn : public EditAggregateTxn +{ +public: + +private: + PlaceholderTxn(); + +public: + + virtual ~PlaceholderTxn(); + + NS_IMETHOD Do(void); + + NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction); + + NS_IMETHOD SetAbsorb(PRBool aAbsorb); + + friend class TransactionFactory; + +protected: + + PRBool mAbsorb; + +}; + +inline NS_IMETHODIMP PlaceholderTxn::SetAbsorb(PRBool aAbsorb) +{ + mAbsorb = aAbsorb; + return NS_OK; +}; + + +#endif diff --git a/editor/base/TransactionFactory.cpp b/editor/base/TransactionFactory.cpp index f6f6b6b771b7..d1f513962663 100644 --- a/editor/base/TransactionFactory.cpp +++ b/editor/base/TransactionFactory.cpp @@ -19,6 +19,7 @@ #include "TransactionFactory.h" // transactions this factory knows how to build #include "EditAggregateTxn.h" +#include "PlaceholderTxn.h" #include "InsertTextTxn.h" #include "DeleteTextTxn.h" #include "CreateElementTxn.h" @@ -39,6 +40,7 @@ #include "JoinTableCellsTxn.h" static NS_DEFINE_IID(kEditAggregateTxnIID, EDIT_AGGREGATE_TXN_IID); +static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID); static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID); static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID); static NS_DEFINE_IID(kCreateElementTxnIID, CREATE_ELEMENT_TXN_IID); @@ -91,6 +93,8 @@ TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult) *aResult = new JoinElementTxn(); else if (aTxnType.Equals(kEditAggregateTxnIID)) *aResult = new EditAggregateTxn(); + else if (aTxnType.Equals(kPlaceholderTxnIID)) + *aResult = new PlaceholderTxn(); else result = NS_ERROR_NO_INTERFACE; diff --git a/editor/base/makefile.win b/editor/base/makefile.win index d4671ec8ef7f..14fb865ae4fa 100644 --- a/editor/base/makefile.win +++ b/editor/base/makefile.win @@ -33,6 +33,7 @@ CPPSRCS = \ ChangeAttributeTxn.cpp \ InsertTextTxn.cpp \ DeleteTextTxn.cpp \ + PlaceholderTxn.cpp \ CreateElementTxn.cpp \ InsertElementTxn.cpp \ DeleteElementTxn.cpp \ @@ -67,6 +68,7 @@ CPP_OBJS = \ .\$(OBJDIR)\ChangeAttributeTxn.obj \ .\$(OBJDIR)\InsertTextTxn.obj \ .\$(OBJDIR)\DeleteTextTxn.obj \ + .\$(OBJDIR)\PlaceholderTxn.obj \ .\$(OBJDIR)\CreateElementTxn.obj \ .\$(OBJDIR)\InsertElementTxn.obj \ .\$(OBJDIR)\DeleteElementTxn.obj \ diff --git a/editor/base/nsEditor.cpp b/editor/base/nsEditor.cpp index 265bfaeb973a..294843f4e742 100644 --- a/editor/base/nsEditor.cpp +++ b/editor/base/nsEditor.cpp @@ -1184,10 +1184,18 @@ NS_IMETHODIMP nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag, nsIDO return result; } #ifdef NS_DEBUG - PRBool testCollapsed; - nsresult debugResult = selection->IsCollapsed(&testCollapsed); - NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); - NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion");; + nsCOMPtrtestSelectedNode; + PRInt32 testOffset; + nsresult debugResult = selection->GetAnchorNodeAndOffset(getter_AddRefs(testSelectedNode), &testOffset); + // no selection is ok. + // if there is a selection, it must be collapsed + if (testSelectedNode) + { + PRBool testCollapsed; + debugResult = selection->IsCollapsed(&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 text node diff --git a/editor/base/nsTextEditRules.cpp b/editor/base/nsTextEditRules.cpp index 8a1accd50169..2668c8155835 100644 --- a/editor/base/nsTextEditRules.cpp +++ b/editor/base/nsTextEditRules.cpp @@ -18,6 +18,8 @@ #include "nsTextEditRules.h" #include "nsTextEditor.h" +#include "PlaceholderTxn.h" +#include "InsertTextTxn.h" #include "nsCOMPtr.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" @@ -31,6 +33,8 @@ const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE"; const static char* kMOZEditorBogusNodeValue="TRUE"; +static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID); + static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag) { nsCOMPtrelement; @@ -115,45 +119,12 @@ nsTextEditRules::WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel) *aCancel = PR_FALSE; // check for the magic content node and delete it if it exists - nsCOMPtrdoc; - mEditor->GetDocument(getter_AddRefs(doc)); - nsCOMPtrnodeList; - nsAutoString bodyTag = "body"; - nsresult result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if ((NS_SUCCEEDED(result)) && nodeList) + if (mBogusNode) { - PRUint32 count; - nodeList->GetLength(&count); - NS_ASSERTION(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtrbodyNode; - result = nodeList->Item(0, getter_AddRefs(bodyNode)); - if ((NS_SUCCEEDED(result)) && bodyNode) - { // now we've got the body tag. - // iterate the body tag, looking for editable content - // if the magic node is found, delete it - PRBool foundBogusContent=PR_TRUE; - nsCOMPtrbodyChild; // a child of the body, for iteration - nsCOMPtrbogusNode; // this will be the magic node - result = bodyNode->GetFirstChild(getter_AddRefs(bodyChild)); - while ((NS_SUCCEEDED(result)) && bodyChild) - { - bogusNode = do_QueryInterface(bodyChild); - if (PR_TRUE==IsEditable(bodyChild)) - { - foundBogusContent = PR_FALSE; - break; - } - nsCOMPtrtemp; - bodyChild->GetNextSibling(getter_AddRefs(temp)); - bodyChild = do_QueryInterface(temp); - } - if (PR_TRUE==foundBogusContent) - { - mEditor->DeleteNode(bogusNode); - // there is no longer any legit selection, so clear it. - aSelection->ClearSelection(); - } - } + mEditor->DeleteNode(mBogusNode); + mBogusNode = do_QueryInterface(nsnull); + // there is no longer any legit selection, so clear it. + aSelection->ClearSelection(); } return NS_OK; @@ -169,13 +140,22 @@ NS_IMETHODIMP nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, const nsString& aInputString, PRBool *aCancel, - nsString& aOutputString) + nsString& aOutputString, + PlaceholderTxn **aTxn) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param *aCancel = PR_FALSE; // by default, we insert what we're told to insert aOutputString = aInputString; + if (mBogusNode) + { + nsresult result = TransactionFactory::GetNewTransaction(kPlaceholderTxnIID, (EditTxn **)aTxn); + if (NS_FAILED(result)) { return result; } + if (!*aTxn) { return NS_ERROR_NULL_POINTER; } + (*aTxn)->SetName(InsertTextTxn::gInsertTextTxnName); + mEditor->Do(*aTxn); + } return WillInsert(aSelection, aCancel); } @@ -315,31 +295,9 @@ nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCance *aCancel = PR_FALSE; // if there is only bogus content, cancel the operation - nsCOMPtrnode; - PRInt32 offset; - nsresult result = aSelection->GetAnchorNodeAndOffset(getter_AddRefs(node), &offset); - if ((NS_SUCCEEDED(result)) && node) - { - nsCOMPtrparent; - parent = do_QueryInterface(node); - while (node) - { //if we find the bogus node, cancel the operation - nsCOMPtrelement; - element = do_QueryInterface(parent); - if (element) - { - nsAutoString att(kMOZEditorBogusNodeAttr); - nsAutoString val; - nsresult result = element->GetAttribute(att, val); - if (val.Equals(kMOZEditorBogusNodeValue)) { - *aCancel = PR_TRUE; - return NS_OK; - } - } - // walk up the content hierarchy - parent->GetParentNode(getter_AddRefs(node)); - parent = do_QueryInterface(node); - } + if (mBogusNode) { + *aCancel = PR_TRUE; + return NS_OK; } return NS_OK; } @@ -389,13 +347,13 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul } if (PR_TRUE==needsBogusContent) { - nsCOMPtrnewPNode; + // set mBogusNode to be the newly created

result = mEditor->CreateNode(nsAutoString("P"), bodyNode, 0, - getter_AddRefs(newPNode)); - if ((NS_SUCCEEDED(result)) && newPNode) + getter_AddRefs(mBogusNode)); + if ((NS_SUCCEEDED(result)) && mBogusNode) { nsCOMPtrnewTNode; - result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newPNode, 0, + result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), mBogusNode, 0, getter_AddRefs(newTNode)); if ((NS_SUCCEEDED(result)) && newTNode) { @@ -411,7 +369,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul } // make sure we know the PNode is bogus nsCOMPtrnewPElement; - newPElement = do_QueryInterface(newPNode); + newPElement = do_QueryInterface(mBogusNode); if (newPElement) { nsAutoString att(kMOZEditorBogusNodeAttr); diff --git a/editor/base/nsTextEditRules.h b/editor/base/nsTextEditRules.h index 36e7885999b4..1c43c957a5e5 100644 --- a/editor/base/nsTextEditRules.h +++ b/editor/base/nsTextEditRules.h @@ -21,9 +21,10 @@ #include "nsIEditor.h" #include "nsCOMPtr.h" +#include "nsIDOMNode.h" class nsTextEditor; - +class PlaceholderTxn; /** Object that encapsulates HTML text-specific editing rules. * @@ -52,7 +53,8 @@ public: NS_IMETHOD WillInsertText(nsIDOMSelection *aSelection, const nsString& aInputString, PRBool *aCancel, - nsString& aOutputString); + nsString& aOutputString, + PlaceholderTxn ** aTxn); NS_IMETHOD DidInsertText(nsIDOMSelection *aSelection, const nsString& aStringToInsert, nsresult aResult); NS_IMETHOD WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel); @@ -64,7 +66,7 @@ public: protected: nsTextEditor *mEditor; // note that we do not refcount the editor - + nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc }; #endif //nsTextEditRules_h__ diff --git a/editor/base/nsTextEditor.cpp b/editor/base/nsTextEditor.cpp index a1d43999faf2..4d5cfdbf1b70 100644 --- a/editor/base/nsTextEditor.cpp +++ b/editor/base/nsTextEditor.cpp @@ -49,6 +49,11 @@ #include "nsIPresShell.h" #include "nsIStyleContext.h" +// transactions the text editor knows how to build itself +#include "TransactionFactory.h" +#include "PlaceholderTxn.h" +#include "InsertTextTxn.h" + class nsIFrame; @@ -430,22 +435,20 @@ NS_IMETHODIMP nsTextEditor::InsertText(const nsString& aStringToInsert) nsCOMPtr selection; PRBool cancel= PR_FALSE; - nsresult result = nsEditor::BeginTransaction(); - if (NS_FAILED(result)) { return result; } - // pre-process nsEditor::GetSelection(getter_AddRefs(selection)); - nsString stringToInsert; - result = mRules->WillInsertText(selection, aStringToInsert, &cancel, stringToInsert); + nsAutoString stringToInsert; + PlaceholderTxn *placeholderTxn=nsnull; + nsresult result = mRules->WillInsertText(selection, aStringToInsert, &cancel, stringToInsert, + &placeholderTxn); if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) { result = nsEditor::InsertText(stringToInsert); // post-process result = mRules->DidInsertText(selection, stringToInsert, result); } - - nsresult endTxnResult = nsEditor::EndTransaction(); // don't return this result! - NS_ASSERTION ((NS_SUCCEEDED(endTxnResult)), "bad end transaction result"); + if (placeholderTxn) + placeholderTxn->SetAbsorb(PR_FALSE); // this ends the merging of txns into placeholderTxn // BEGIN HACK!!! HACKForceRedraw(); diff --git a/editor/libeditor/base/DeleteRangeTxn.cpp b/editor/libeditor/base/DeleteRangeTxn.cpp index ee00372d2e42..063cc1448b42 100644 --- a/editor/libeditor/base/DeleteRangeTxn.cpp +++ b/editor/libeditor/base/DeleteRangeTxn.cpp @@ -167,7 +167,8 @@ NS_IMETHODIMP DeleteRangeTxn::Undo(void) nsCOMPtr selection; result = mEditor->GetSelection(getter_AddRefs(selection)); if (NS_SUCCEEDED(result)) { - result = selection->Collapse(mStartParent, mStartOffset); + selection->Collapse(mStartParent, mStartOffset); + selection->Extend(mEndParent, mEndOffset); } } diff --git a/editor/libeditor/base/EditAggregateTxn.cpp b/editor/libeditor/base/EditAggregateTxn.cpp index 4fc8b1c8f558..e6a93f29632f 100644 --- a/editor/libeditor/base/EditAggregateTxn.cpp +++ b/editor/libeditor/base/EditAggregateTxn.cpp @@ -153,26 +153,6 @@ NS_IMETHODIMP EditAggregateTxn::AppendChild(EditTxn *aTxn) return NS_ERROR_NULL_POINTER; } -NS_IMETHODIMP EditAggregateTxn::SetName(nsIAtom *aName) -{ - mName = do_QueryInterface(aName); - return NS_OK; -} - -NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName) -{ - if (aName) - { - if (mName) - { - *aName = mName; - NS_ADDREF(*aName); - return NS_OK; - } - } - return NS_ERROR_NULL_POINTER; -} - NS_IMETHODIMP EditAggregateTxn::GetCount(PRInt32 *aCount) { if (!aCount) { @@ -205,6 +185,28 @@ NS_IMETHODIMP EditAggregateTxn::GetTxnAt(PRInt32 aIndex, EditTxn **aTxn) } +NS_IMETHODIMP EditAggregateTxn::SetName(nsIAtom *aName) +{ + mName = do_QueryInterface(aName); + return NS_OK; +} + +NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName) +{ + if (aName) + { + if (mName) + { + *aName = mName; + NS_ADDREF(*aName); + return NS_OK; + } + } + return NS_ERROR_NULL_POINTER; +} + + + diff --git a/editor/libeditor/base/EditAggregateTxn.h b/editor/libeditor/base/EditAggregateTxn.h index 1ad6a5442c75..2e83a62e4a5b 100644 --- a/editor/libeditor/base/EditAggregateTxn.h +++ b/editor/libeditor/base/EditAggregateTxn.h @@ -71,10 +71,10 @@ public: */ NS_IMETHOD GetTxnAt(PRInt32 aIndex, EditTxn **aTxn); - /** set the name assigned to this aggregate txn */ + /** set the name assigned to this txn */ NS_IMETHOD SetName(nsIAtom *aName); - /** get the name assigned to this aggregate txn */ + /** get the name assigned to this txn */ NS_IMETHOD GetName(nsIAtom **aName); protected: @@ -82,7 +82,6 @@ protected: //XXX: if this was an nsISupportsArray, it would handle refcounting for us nsVoidArray * mChildren; nsCOMPtr mName; - }; #endif diff --git a/editor/libeditor/base/EditTxn.h b/editor/libeditor/base/EditTxn.h index 4323ef56bc57..60fbb57b862a 100644 --- a/editor/libeditor/base/EditTxn.h +++ b/editor/libeditor/base/EditTxn.h @@ -20,8 +20,7 @@ #define EditTxn_h__ #include "nsITransaction.h" - -class nsIDOMNode; +#include "nsCOMPtr.h" #define EDIT_TXN_IID \ {/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \ @@ -31,6 +30,8 @@ class nsIDOMNode; /** * base class for all document editing transactions. * provides default concrete behavior for all nsITransaction methods. + * EditTxns optionally have a name. This name is for internal purposes only, + * it is never seen by the user or by any external entity. */ class EditTxn : public nsITransaction { diff --git a/editor/libeditor/base/InsertTextTxn.cpp b/editor/libeditor/base/InsertTextTxn.cpp index b7b718ae0996..70c86cfc48a1 100644 --- a/editor/libeditor/base/InsertTextTxn.cpp +++ b/editor/libeditor/base/InsertTextTxn.cpp @@ -119,19 +119,23 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti otherTxn->GetName(getter_AddRefs(txnName)); if (txnName.get()==gInsertTextTxnName) { // yep, it's one of ours. By definition, it must contain only - // a single InsertTextTxn + // another aggregate with a single child, + // or a single InsertTextTxn nsCOMPtr childTxn; otherTxn->GetTxnAt(0, getter_AddRefs(childTxn)); - nsCOMPtr otherInsertTxn; - otherInsertTxn = do_QueryInterface(childTxn, &result); - if (otherInsertTxn) + if (childTxn) { - if (PR_TRUE==IsSequentialInsert(otherInsertTxn)) + nsCOMPtr otherInsertTxn; + otherInsertTxn = do_QueryInterface(childTxn, &result); + if (otherInsertTxn) { - nsAutoString otherData; - otherInsertTxn->GetData(otherData); - mStringToInsert += otherData; - *aDidMerge = PR_TRUE; + if (PR_TRUE==IsSequentialInsert(otherInsertTxn)) + { + nsAutoString otherData; + otherInsertTxn->GetData(otherData); + mStringToInsert += otherData; + *aDidMerge = PR_TRUE; + } } } } diff --git a/editor/libeditor/base/PlaceholderTxn.cpp b/editor/libeditor/base/PlaceholderTxn.cpp new file mode 100644 index 000000000000..941d6e05c955 --- /dev/null +++ b/editor/libeditor/base/PlaceholderTxn.cpp @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "PlaceholderTxn.h" +#include "nsVoidArray.h" + +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + + +PlaceholderTxn::PlaceholderTxn() + : EditAggregateTxn() +{ + mAbsorb=PR_TRUE; +} + + +PlaceholderTxn::~PlaceholderTxn() +{ +} + +NS_IMETHODIMP PlaceholderTxn::Do(void) +{ + return NS_OK; +} + +NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) +{ + // set out param default value + if (nsnull!=aDidMerge) + *aDidMerge=PR_FALSE; + nsresult result = NS_OK; + if ((nsnull!=aDidMerge) && (nsnull!=aTransaction)) + { + EditTxn *editTxn = (EditTxn*)aTransaction; //XXX: hack, not safe! need nsIEditTransaction! + if (PR_TRUE==mAbsorb) + { // yep, it's one of ours. Assimilate it. + AppendChild(editTxn); + *aDidMerge = PR_TRUE; + } + else + { // let our last child txn make the choice + PRInt32 count = mChildren->Count(); + if (0ElementAt(count-1)); + if (lastTxn) + { + lastTxn->Merge(aDidMerge, aTransaction); + } + } + } + } + return result; +} + diff --git a/editor/libeditor/base/PlaceholderTxn.h b/editor/libeditor/base/PlaceholderTxn.h new file mode 100644 index 000000000000..6f6f39953a39 --- /dev/null +++ b/editor/libeditor/base/PlaceholderTxn.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef AggregatePlaceholderTxn_h__ +#define AggregatePlaceholderTxn_h__ + +#include "EditAggregateTxn.h" + +#define PLACEHOLDER_TXN_IID \ +{/* {0CE9FB00-D9D1-11d2-86DE-000064657374} */ \ +0x0CE9FB00, 0xD9D1, 0x11d2, \ +{0x86, 0xde, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + +/** + * An aggregate transaction that knows how to absorb all subsequent + * transactions with the same name. This transaction does not "Do" anything. + * But it absorbs other transactions via merge, and can undo/redo the + * transactions it has absorbed. + */ +class PlaceholderTxn : public EditAggregateTxn +{ +public: + +private: + PlaceholderTxn(); + +public: + + virtual ~PlaceholderTxn(); + + NS_IMETHOD Do(void); + + NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction); + + NS_IMETHOD SetAbsorb(PRBool aAbsorb); + + friend class TransactionFactory; + +protected: + + PRBool mAbsorb; + +}; + +inline NS_IMETHODIMP PlaceholderTxn::SetAbsorb(PRBool aAbsorb) +{ + mAbsorb = aAbsorb; + return NS_OK; +}; + + +#endif diff --git a/editor/libeditor/base/TransactionFactory.cpp b/editor/libeditor/base/TransactionFactory.cpp index f6f6b6b771b7..d1f513962663 100644 --- a/editor/libeditor/base/TransactionFactory.cpp +++ b/editor/libeditor/base/TransactionFactory.cpp @@ -19,6 +19,7 @@ #include "TransactionFactory.h" // transactions this factory knows how to build #include "EditAggregateTxn.h" +#include "PlaceholderTxn.h" #include "InsertTextTxn.h" #include "DeleteTextTxn.h" #include "CreateElementTxn.h" @@ -39,6 +40,7 @@ #include "JoinTableCellsTxn.h" static NS_DEFINE_IID(kEditAggregateTxnIID, EDIT_AGGREGATE_TXN_IID); +static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID); static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID); static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID); static NS_DEFINE_IID(kCreateElementTxnIID, CREATE_ELEMENT_TXN_IID); @@ -91,6 +93,8 @@ TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult) *aResult = new JoinElementTxn(); else if (aTxnType.Equals(kEditAggregateTxnIID)) *aResult = new EditAggregateTxn(); + else if (aTxnType.Equals(kPlaceholderTxnIID)) + *aResult = new PlaceholderTxn(); else result = NS_ERROR_NO_INTERFACE; diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 265bfaeb973a..294843f4e742 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -1184,10 +1184,18 @@ NS_IMETHODIMP nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag, nsIDO return result; } #ifdef NS_DEBUG - PRBool testCollapsed; - nsresult debugResult = selection->IsCollapsed(&testCollapsed); - NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); - NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion");; + nsCOMPtrtestSelectedNode; + PRInt32 testOffset; + nsresult debugResult = selection->GetAnchorNodeAndOffset(getter_AddRefs(testSelectedNode), &testOffset); + // no selection is ok. + // if there is a selection, it must be collapsed + if (testSelectedNode) + { + PRBool testCollapsed; + debugResult = selection->IsCollapsed(&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 text node diff --git a/editor/libeditor/text/nsTextEditRules.cpp b/editor/libeditor/text/nsTextEditRules.cpp index 8a1accd50169..2668c8155835 100644 --- a/editor/libeditor/text/nsTextEditRules.cpp +++ b/editor/libeditor/text/nsTextEditRules.cpp @@ -18,6 +18,8 @@ #include "nsTextEditRules.h" #include "nsTextEditor.h" +#include "PlaceholderTxn.h" +#include "InsertTextTxn.h" #include "nsCOMPtr.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" @@ -31,6 +33,8 @@ const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE"; const static char* kMOZEditorBogusNodeValue="TRUE"; +static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID); + static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag) { nsCOMPtrelement; @@ -115,45 +119,12 @@ nsTextEditRules::WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel) *aCancel = PR_FALSE; // check for the magic content node and delete it if it exists - nsCOMPtrdoc; - mEditor->GetDocument(getter_AddRefs(doc)); - nsCOMPtrnodeList; - nsAutoString bodyTag = "body"; - nsresult result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if ((NS_SUCCEEDED(result)) && nodeList) + if (mBogusNode) { - PRUint32 count; - nodeList->GetLength(&count); - NS_ASSERTION(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtrbodyNode; - result = nodeList->Item(0, getter_AddRefs(bodyNode)); - if ((NS_SUCCEEDED(result)) && bodyNode) - { // now we've got the body tag. - // iterate the body tag, looking for editable content - // if the magic node is found, delete it - PRBool foundBogusContent=PR_TRUE; - nsCOMPtrbodyChild; // a child of the body, for iteration - nsCOMPtrbogusNode; // this will be the magic node - result = bodyNode->GetFirstChild(getter_AddRefs(bodyChild)); - while ((NS_SUCCEEDED(result)) && bodyChild) - { - bogusNode = do_QueryInterface(bodyChild); - if (PR_TRUE==IsEditable(bodyChild)) - { - foundBogusContent = PR_FALSE; - break; - } - nsCOMPtrtemp; - bodyChild->GetNextSibling(getter_AddRefs(temp)); - bodyChild = do_QueryInterface(temp); - } - if (PR_TRUE==foundBogusContent) - { - mEditor->DeleteNode(bogusNode); - // there is no longer any legit selection, so clear it. - aSelection->ClearSelection(); - } - } + mEditor->DeleteNode(mBogusNode); + mBogusNode = do_QueryInterface(nsnull); + // there is no longer any legit selection, so clear it. + aSelection->ClearSelection(); } return NS_OK; @@ -169,13 +140,22 @@ NS_IMETHODIMP nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, const nsString& aInputString, PRBool *aCancel, - nsString& aOutputString) + nsString& aOutputString, + PlaceholderTxn **aTxn) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param *aCancel = PR_FALSE; // by default, we insert what we're told to insert aOutputString = aInputString; + if (mBogusNode) + { + nsresult result = TransactionFactory::GetNewTransaction(kPlaceholderTxnIID, (EditTxn **)aTxn); + if (NS_FAILED(result)) { return result; } + if (!*aTxn) { return NS_ERROR_NULL_POINTER; } + (*aTxn)->SetName(InsertTextTxn::gInsertTextTxnName); + mEditor->Do(*aTxn); + } return WillInsert(aSelection, aCancel); } @@ -315,31 +295,9 @@ nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCance *aCancel = PR_FALSE; // if there is only bogus content, cancel the operation - nsCOMPtrnode; - PRInt32 offset; - nsresult result = aSelection->GetAnchorNodeAndOffset(getter_AddRefs(node), &offset); - if ((NS_SUCCEEDED(result)) && node) - { - nsCOMPtrparent; - parent = do_QueryInterface(node); - while (node) - { //if we find the bogus node, cancel the operation - nsCOMPtrelement; - element = do_QueryInterface(parent); - if (element) - { - nsAutoString att(kMOZEditorBogusNodeAttr); - nsAutoString val; - nsresult result = element->GetAttribute(att, val); - if (val.Equals(kMOZEditorBogusNodeValue)) { - *aCancel = PR_TRUE; - return NS_OK; - } - } - // walk up the content hierarchy - parent->GetParentNode(getter_AddRefs(node)); - parent = do_QueryInterface(node); - } + if (mBogusNode) { + *aCancel = PR_TRUE; + return NS_OK; } return NS_OK; } @@ -389,13 +347,13 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul } if (PR_TRUE==needsBogusContent) { - nsCOMPtrnewPNode; + // set mBogusNode to be the newly created

result = mEditor->CreateNode(nsAutoString("P"), bodyNode, 0, - getter_AddRefs(newPNode)); - if ((NS_SUCCEEDED(result)) && newPNode) + getter_AddRefs(mBogusNode)); + if ((NS_SUCCEEDED(result)) && mBogusNode) { nsCOMPtrnewTNode; - result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newPNode, 0, + result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), mBogusNode, 0, getter_AddRefs(newTNode)); if ((NS_SUCCEEDED(result)) && newTNode) { @@ -411,7 +369,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul } // make sure we know the PNode is bogus nsCOMPtrnewPElement; - newPElement = do_QueryInterface(newPNode); + newPElement = do_QueryInterface(mBogusNode); if (newPElement) { nsAutoString att(kMOZEditorBogusNodeAttr); diff --git a/editor/libeditor/text/nsTextEditRules.h b/editor/libeditor/text/nsTextEditRules.h index 36e7885999b4..1c43c957a5e5 100644 --- a/editor/libeditor/text/nsTextEditRules.h +++ b/editor/libeditor/text/nsTextEditRules.h @@ -21,9 +21,10 @@ #include "nsIEditor.h" #include "nsCOMPtr.h" +#include "nsIDOMNode.h" class nsTextEditor; - +class PlaceholderTxn; /** Object that encapsulates HTML text-specific editing rules. * @@ -52,7 +53,8 @@ public: NS_IMETHOD WillInsertText(nsIDOMSelection *aSelection, const nsString& aInputString, PRBool *aCancel, - nsString& aOutputString); + nsString& aOutputString, + PlaceholderTxn ** aTxn); NS_IMETHOD DidInsertText(nsIDOMSelection *aSelection, const nsString& aStringToInsert, nsresult aResult); NS_IMETHOD WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel); @@ -64,7 +66,7 @@ public: protected: nsTextEditor *mEditor; // note that we do not refcount the editor - + nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc }; #endif //nsTextEditRules_h__