diff --git a/mozilla/editor/base/ChangeAttributeTxn.cpp b/mozilla/editor/base/ChangeAttributeTxn.cpp index 234e9031c84..e3cc0e8fb0b 100644 --- a/mozilla/editor/base/ChangeAttributeTxn.cpp +++ b/mozilla/editor/base/ChangeAttributeTxn.cpp @@ -17,23 +17,33 @@ */ #include "ChangeAttributeTxn.h" -#include "editor.h" #include "nsIDOMElement.h" +#include "editor.h" -// note that aEditor is not refcounted -ChangeAttributeTxn::ChangeAttributeTxn(nsEditor *aEditor, - nsIDOMElement *aElement, - const nsString& aAttribute, - const nsString& aValue, - PRBool aRemoveAttribute) - : EditTxn(aEditor) +ChangeAttributeTxn::ChangeAttributeTxn() + : EditTxn() { - mElement = aElement; - mAttribute = aAttribute; - mValue = aValue; - mRemoveAttribute = aRemoveAttribute; - mAttributeWasSet=PR_FALSE; - mUndoValue=""; +} + +nsresult ChangeAttributeTxn::Init(nsIEditor *aEditor, + nsIDOMElement *aElement, + const nsString& aAttribute, + const nsString& aValue, + PRBool aRemoveAttribute) +{ + if (nsnull!=aEditor && nsnull!=aElement) + { + mEditor = aEditor; + mElement = aElement; + mAttribute = aAttribute; + mValue = aValue; + mRemoveAttribute = aRemoveAttribute; + mAttributeWasSet=PR_FALSE; + mUndoValue=""; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; } nsresult ChangeAttributeTxn::Do(void) diff --git a/mozilla/editor/base/ChangeAttributeTxn.h b/mozilla/editor/base/ChangeAttributeTxn.h index 689d991f3c0..48bded0e116 100644 --- a/mozilla/editor/base/ChangeAttributeTxn.h +++ b/mozilla/editor/base/ChangeAttributeTxn.h @@ -20,8 +20,14 @@ #define ChangeAttributeTxn_h__ #include "EditTxn.h" +#include "nsCOMPtr.h" +#include "nsIDOMElement.h" +#include "nsIEditor.h" -class nsIDOMElement; +#define CHANGE_ATTRIBUTE_TXN_IID \ +{/* 97818860-ac48-11d2-86d8-000064657374 */ \ +0x97818860, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that changes an attribute of a content node. @@ -31,11 +37,16 @@ class ChangeAttributeTxn : public EditTxn { public: - ChangeAttributeTxn(nsEditor *aEditor, - nsIDOMElement *aElement, - const nsString& aAttribute, - const nsString& aValue, - PRBool aRemoveAttribute); + virtual nsresult Init(nsIEditor *aEditor, + nsIDOMElement *aElement, + const nsString& aAttribute, + const nsString& aValue, + PRBool aRemoveAttribute); + +private: + ChangeAttributeTxn(); + +public: virtual nsresult Do(void); @@ -54,9 +65,12 @@ public: virtual nsresult GetRedoString(nsString **aString); protected: + + /** the editor that created this transaction */ + nsCOMPtr mEditor; /** the element to operate upon */ - nsIDOMElement *mElement; + nsCOMPtr mElement; /** the attribute to change */ nsString mAttribute; @@ -72,6 +86,8 @@ protected: /** PR_TRUE if the operation is to remove mAttribute from mElement */ PRBool mRemoveAttribute; + + friend class TransactionFactory; }; #endif diff --git a/mozilla/editor/base/CreateElementTxn.cpp b/mozilla/editor/base/CreateElementTxn.cpp index c62adf8b423..44f1b69170a 100644 --- a/mozilla/editor/base/CreateElementTxn.cpp +++ b/mozilla/editor/base/CreateElementTxn.cpp @@ -17,52 +17,50 @@ */ #include "CreateElementTxn.h" -#include "editor.h" -#include "nsIDOMNode.h" #include "nsIDOMNodeList.h" -#include "nsIDOMDocument.h" -#include "nsIDOMElement.h" -// note that aEditor is not refcounted -CreateElementTxn::CreateElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - const nsString& aTag, - nsIDOMNode *aParent, - PRUint32 aOffsetInParent) - : EditTxn(aEditor) +CreateElementTxn::CreateElementTxn() + : EditTxn() { - mDoc = aDoc; - NS_ADDREF(mDoc); - mTag = aTag; - mParent = aParent; - NS_ADDREF(mParent); - mOffsetInParent = aOffsetInParent; - mNewNode = nsnull; - mRefNode = nsnull; } +nsresult CreateElementTxn::Init(nsIDOMDocument *aDoc, + const nsString& aTag, + nsIDOMNode *aParent, + PRUint32 aOffsetInParent) +{ + if ((nsnull!=aDoc) && (nsnull!=aParent)) + { + mDoc = aDoc; + mTag = aTag; + mParent = aParent; + mOffsetInParent = aOffsetInParent; + mNewNode = nsnull; + mRefNode = nsnull; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; +} + + CreateElementTxn::~CreateElementTxn() { - NS_IF_RELEASE(mDoc); - NS_IF_RELEASE(mParent); - NS_IF_RELEASE(mNewNode); } nsresult CreateElementTxn::Do(void) { // create a new node - nsresult result = mDoc->CreateElement(mTag, &mNewNode); - NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); + nsresult result = mDoc->CreateElement(mTag, getter_AddRefs(mNewNode)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); - if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) + if ((NS_SUCCEEDED(result)) && (mNewNode)) { // insert the new node - nsIDOMNode *resultNode=nsnull; + nsCOMPtr resultNode; if (CreateElementTxn::eAppend==mOffsetInParent) { - result = mParent->AppendChild(mNewNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); // this object already holds a ref from CreateElement + result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode)); } else { @@ -70,12 +68,10 @@ nsresult CreateElementTxn::Do(void) result = mParent->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { - result = childNodes->Item(mOffsetInParent, &mRefNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=mRefNode)) + result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); + if ((NS_SUCCEEDED(result)) && (mRefNode)) { - result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); // this object already holds a ref from CreateElement + result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); } } } @@ -85,20 +81,16 @@ nsresult CreateElementTxn::Do(void) nsresult CreateElementTxn::Undo(void) { - nsIDOMNode *resultNode=nsnull; - nsresult result = mParent->RemoveChild(mNewNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); + nsCOMPtr resultNode; + nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); return result; } nsresult CreateElementTxn::Redo(void) { - nsIDOMNode *resultNode=nsnull; - nsresult result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); - return result; + nsCOMPtr resultNode; + nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + return result; } nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient) diff --git a/mozilla/editor/base/CreateElementTxn.h b/mozilla/editor/base/CreateElementTxn.h index 269de9c3f5a..bbe1c4a4421 100644 --- a/mozilla/editor/base/CreateElementTxn.h +++ b/mozilla/editor/base/CreateElementTxn.h @@ -20,10 +20,15 @@ #define CreateElementTxn_h__ #include "EditTxn.h" +#include "nsIDOMDocument.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsCOMPtr.h" -class nsIDOMDocument; -class nsIDOMNode; -class nsIDOMElement; +#define CREATE_ELEMENT_TXN_IID \ +{/* 7a6393c0-ac48-11d2-86d8-000064657374 */ \ +0x7a6393c0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that creates a new node in the content tree. @@ -34,11 +39,15 @@ public: enum { eAppend=-1 }; - CreateElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - const nsString& aTag, - nsIDOMNode *aParent, - PRUint32 aOffsetInParent); + virtual nsresult Init(nsIDOMDocument *aDoc, + const nsString& aTag, + nsIDOMNode *aParent, + PRUint32 aOffsetInParent); + +private: + CreateElementTxn(); + +public: virtual ~CreateElementTxn(); @@ -61,22 +70,24 @@ public: protected: /** the document into which the new node will be inserted */ - nsIDOMDocument *mDoc; + nsCOMPtr mDoc; /** the tag (mapping to object type) for the new element */ nsString mTag; /** the node into which the new node will be inserted */ - nsIDOMNode *mParent; + nsCOMPtr mParent; /** the index in mParent for the new node */ PRUint32 mOffsetInParent; /** the new node to insert */ - nsIDOMElement *mNewNode; + nsCOMPtr mNewNode; /** the node we will insert mNewNode before. We compute this ourselves. */ - nsIDOMNode *mRefNode; + nsCOMPtr mRefNode; + + friend class TransactionFactory; }; diff --git a/mozilla/editor/base/DeleteElementTxn.cpp b/mozilla/editor/base/DeleteElementTxn.cpp index 85ca3462ebb..5b55f78a54f 100644 --- a/mozilla/editor/base/DeleteElementTxn.cpp +++ b/mozilla/editor/base/DeleteElementTxn.cpp @@ -17,30 +17,32 @@ */ #include "DeleteElementTxn.h" -#include "editor.h" -#include "nsIDOMDocument.h" +#ifdef NS_DEBUG #include "nsIDOMElement.h" +#endif -// note that aEditor is not refcounted -DeleteElementTxn::DeleteElementTxn(nsEditor * aEditor, - nsIDOMDocument *aDoc, - nsIDOMNode * aElement, - nsIDOMNode * aParent) - : EditTxn(aEditor) + +DeleteElementTxn::DeleteElementTxn() + : EditTxn() { - mDoc = aDoc; - NS_ADDREF(mDoc); - mElement = aElement; - NS_ADDREF(mElement); - mParent = aParent; - NS_ADDREF(mParent); } +nsresult DeleteElementTxn::Init(nsIDOMNode *aElement, + nsIDOMNode *aParent) +{ + if ((nsnull!=aElement) && (nsnull!=aParent)) + { + mElement = aElement; + mParent = aParent; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; +} + + DeleteElementTxn::~DeleteElementTxn() { - NS_IF_RELEASE(mDoc); - NS_IF_RELEASE(mParent); - NS_IF_RELEASE(mElement); } nsresult DeleteElementTxn::Do(void) @@ -48,11 +50,32 @@ nsresult DeleteElementTxn::Do(void) if (!mParent || !mElement) return NS_ERROR_NULL_POINTER; #ifdef NS_DEBUG + // begin debug output + nsCOMPtr element=mElement; + nsAutoString elementTag="text node"; + if (element) + element->GetTagName(elementTag); + nsCOMPtr parentElement=mParent; + nsAutoString parentElementTag="text node"; + if (parentElement) + parentElement->GetTagName(parentElementTag); + char *c, *p; + c = elementTag.ToNewCString(); + p = parentElementTag.ToNewCString(); + if (c&&p) + { + printf("DeleteElementTxn: deleting child %s from parent %s\n", c, p); + delete [] c; + delete [] p; + } + // end debug output + // begin sanity check 1: parent-child relationship nsresult testResult; nsCOMPtr parentNode; testResult = mElement->GetParentNode(getter_AddRefs(parentNode)); NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent"); NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() "); + // end sanity check 1. #endif // remember which child mElement was (by remembering which child was next) diff --git a/mozilla/editor/base/DeleteElementTxn.h b/mozilla/editor/base/DeleteElementTxn.h index dce2b04dcda..9cf3469f918 100644 --- a/mozilla/editor/base/DeleteElementTxn.h +++ b/mozilla/editor/base/DeleteElementTxn.h @@ -23,8 +23,10 @@ #include "nsIDOMNode.h" #include "nsCOMPtr.h" -class nsIDOMDocument; -class nsIDOMElement; +#define DELETE_ELEMENT_TXN_IID \ +{/* 6fd77770-ac49-11d2-86d8-000064657374 */ \ +0x6fd77770, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that deletes a single element @@ -33,10 +35,13 @@ class DeleteElementTxn : public EditTxn { public: - DeleteElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - nsIDOMNode *aElement, - nsIDOMNode *aParent); + virtual nsresult Init(nsIDOMNode *aElement, + nsIDOMNode *aParent); + +private: + DeleteElementTxn(); + +public: virtual ~DeleteElementTxn(); @@ -58,14 +63,11 @@ public: protected: - /** the document into which the new node will be inserted */ - nsIDOMDocument *mDoc; - /** the element to delete */ - nsIDOMNode *mElement; + nsCOMPtr mElement; /** the node into which the new node will be inserted */ - nsIDOMNode *mParent; + nsCOMPtr mParent; /** the index in mParent for the new node */ PRUint32 mOffsetInParent; @@ -73,6 +75,8 @@ protected: /** the node we will insert mNewNode before. We compute this ourselves. */ nsCOMPtr mRefNode; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/base/DeleteRangeTxn.cpp b/mozilla/editor/base/DeleteRangeTxn.cpp index 84a1b397e4d..37a6e286434 100644 --- a/mozilla/editor/base/DeleteRangeTxn.cpp +++ b/mozilla/editor/base/DeleteRangeTxn.cpp @@ -17,18 +17,73 @@ */ #include "DeleteRangeTxn.h" -#include "editor.h" #include "nsIDOMRange.h" +#include "nsIDOMCharacterData.h" +#include "nsIDOMNodeList.h" +#include "DeleteTextTxn.h" +#include "DeleteElementTxn.h" +#include "TransactionFactory.h" +#include "nsISupportsArray.h" + +static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID); +static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); + // note that aEditor is not refcounted -DeleteRangeTxn::DeleteRangeTxn(nsEditor *aEditor, - nsIDOMRange *aRange) - : EditTxn(aEditor) +DeleteRangeTxn::DeleteRangeTxn() + : EditAggregateTxn() { - aRange->GetStartParent(getter_AddRefs(mStartParent)); - aRange->GetEndParent(getter_AddRefs(mEndParent)); - aRange->GetStartOffset(&mStartOffset); - aRange->GetEndOffset(&mEndOffset); +} + +nsresult DeleteRangeTxn::Init(nsIDOMRange *aRange) +{ + if (nsnull!=aRange) + { + nsresult result = aRange->GetStartParent(getter_AddRefs(mStartParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed."); + result = aRange->GetEndParent(getter_AddRefs(mEndParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed."); + result = aRange->GetStartOffset(&mStartOffset); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed."); + result = aRange->GetEndOffset(&mEndOffset); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed."); + result = aRange->GetCommonParent(getter_AddRefs(mCommonParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed."); + +#ifdef NS_DEBUG + PRUint32 count; + nsCOMPtr textNode; + textNode = mStartParent; + if (textNode) + textNode->GetLength(&count); + else + { + nsCOMPtr children; + result = mStartParent->GetChildNodes(getter_AddRefs(children)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad start child list"); + children->GetLength(&count); + } + NS_ASSERTION(mStartOffsetGetLength(&count); + else + { + nsCOMPtr children; + result = mEndParent->GetChildNodes(getter_AddRefs(children)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad end child list"); + children->GetLength(&count); + } + NS_ASSERTION(mEndOffset textNode; + textNode = aStartParent; // this uses implicit nsCOMPtr QI + if (textNode) + { // if the node is a text node, then delete text content + DeleteTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(textNode, aStartOffset, (aEndOffset-aStartOffset)+1); + AppendChild(txn); + } + } + else + { + PRUint32 childCount; + nsCOMPtr children; + result = aStartParent->GetChildNodes(getter_AddRefs(children)); + if ((NS_SUCCEEDED(result)) && children) + { + children->GetLength(&childCount); + NS_ASSERTION(aEndOffset child; + result = children->Item(i, getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, aStartParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + } + } + return result; +} + +nsresult DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, + PRUint32 aOffset, + nsIEditor::Direction aDir) +{ + nsresult result; + // see what kind of node we have + nsCOMPtr textNode; + textNode = aParent; // this uses implicit nsCOMPtr QI + if (textNode) + { // if the node is a text node, then delete text content + PRUint32 start, numToDelete; + if (nsIEditor::eLTR==aDir) + { + start=aOffset; + textNode->GetLength(&numToDelete); + numToDelete -= (aOffset+1); + } + else + { + start=0; + numToDelete=aOffset; + } + DeleteTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(textNode, start, numToDelete); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + else + { // we have an interior node, so delete some of its children + if (nsIEditor::eLTR==aDir) + { // delete from aOffset to end + PRUint32 childCount; + nsCOMPtr children; + result = aParent->GetChildNodes(getter_AddRefs(children)); + if ((NS_SUCCEEDED(result)) && children) + { + children->GetLength(&childCount); + PRUint32 i; + for (i=aOffset; i child; + result = children->Item(i, getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, aParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + } + } + else + { // delete from 0 to aOffset + nsCOMPtr child; + result = aParent->GetFirstChild(getter_AddRefs(child)); + for (PRUint32 i=0; iInit(child, aParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + nsCOMPtr temp = child; + result = temp->GetNextSibling(getter_AddRefs(child)); + } + } + } + + return result; +} + +nsresult DeleteRangeTxn::CreateTxnsToDeleteNodesBetween(nsIDOMNode *aCommonParent, + nsIDOMNode *aFirstChild, + nsIDOMNode *aLastChild) +{ + nsresult result; + PRBool needToProcessLastChild=PR_TRUE; // set to false if we discover we can delete all required nodes by just walking up aFirstChild's parent list + nsCOMPtr parent; // the current parent in the iteration up the ancestors + nsCOMPtr child; // the current child of parent + nsISupportsArray *ancestorList; // the ancestorList of the other endpoint, used to gate deletion + NS_NewISupportsArray(&ancestorList); + if (nsnull==ancestorList) + return NS_ERROR_NULL_POINTER; + + // Walk up the parent list of aFirstChild to aCommonParent, + // deleting all siblings to the right of the ancestors of aFirstChild. + BuildAncestorList(aLastChild, ancestorList); + child = aFirstChild; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && parent) + { + while ((NS_SUCCEEDED(result)) && child) + { // this loop starts with the first sibling of an ancestor of aFirstChild + nsCOMPtr temp = child; + result = temp->GetNextSibling(getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + if (child==aLastChild) + { // aFirstChild and aLastChild have the same parent, and we've reached aLastChild + needToProcessLastChild = PR_FALSE; + break; + } + // test if child is an ancestor of the other node. If it is, don't process this parent anymore + PRInt32 index; + index = ancestorList->IndexOf((nsISupports*)child); + if (-1!=index) + break; + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, parent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + if (parent==aCommonParent) + break; + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + + // Walk up the parent list of aLastChild to aCommonParent, + // deleting all siblings to the left of the ancestors of aLastChild. + BuildAncestorList(aFirstChild, ancestorList); + if (PR_TRUE==needToProcessLastChild) + { + child = aLastChild; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && parent) + { + while ((NS_SUCCEEDED(result)) && child) + { // this loop starts with the first sibling of an ancestor of aFirstChild + nsCOMPtr temp = child; + result = temp->GetPreviousSibling(getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + // test if child is an ancestor of the other node. If it is, don't process this parent anymore + PRInt32 index; + index = ancestorList->IndexOf((nsISupports*)child); + if (-1!=index) + break; + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, parent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + if (parent==aCommonParent) + break; + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + } + NS_RELEASE(ancestorList); + return result; +} + +nsresult DeleteRangeTxn::BuildAncestorList(nsIDOMNode *aNode, nsISupportsArray *aList) +{ + nsresult result=NS_OK; + if (nsnull!=aNode && nsnull!=aList) + { + aList->Clear(); + nsCOMPtr parent; + nsCOMPtr child = aNode; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && child && parent) + { + nsISupports * parentAsISupports; + parent->QueryInterface(nsISupports::IID(), (void **)&parentAsISupports); + aList->AppendElement(parentAsISupports); + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + } + else + result = NS_ERROR_NULL_POINTER; + + return result; +} + + diff --git a/mozilla/editor/base/DeleteRangeTxn.h b/mozilla/editor/base/DeleteRangeTxn.h index 675049f550a..09af0e639c6 100644 --- a/mozilla/editor/base/DeleteRangeTxn.h +++ b/mozilla/editor/base/DeleteRangeTxn.h @@ -19,22 +19,33 @@ #ifndef DeleteRangeTxn_h__ #define DeleteRangeTxn_h__ -#include "EditTxn.h" +#include "EditAggregateTxn.h" #include "nsIDOMNode.h" +#include "nsIEditor.h" #include "nsCOMPtr.h" class nsIDOMDocument; class nsIDOMRange; +class nsISupportsArray; + +#define DELETE_RANGE_TXN_IID \ +{/* 5ec6b260-ac49-11d2-86d8-000064657374 */ \ +0x5ec6b260, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that deletes an entire range in the content tree */ -class DeleteRangeTxn : public EditTxn +class DeleteRangeTxn : public EditAggregateTxn { public: - DeleteRangeTxn(nsEditor *aEditor, - nsIDOMRange *aRange); + virtual nsresult Init(nsIDOMRange *aRange); + +private: + DeleteRangeTxn(); + +public: virtual ~DeleteRangeTxn(); @@ -54,6 +65,23 @@ public: virtual nsresult GetRedoString(nsString **aString); +protected: + + virtual nsresult CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, + PRUint32 aStartOffset, + PRUint32 aEndOffset); + + virtual nsresult CreateTxnsToDeleteNodesBetween(nsIDOMNode *aParent, + nsIDOMNode *aFirstChild, + nsIDOMNode *aLastChild); + + virtual nsresult CreateTxnsToDeleteContent(nsIDOMNode *aParent, + PRUint32 aOffset, + nsIEditor::Direction aDir); + + virtual nsresult BuildAncestorList(nsIDOMNode *aNode, + nsISupportsArray *aList); + protected: /** p1 in the range */ @@ -65,9 +93,14 @@ protected: /** p2 in the range */ nsCOMPtr mEndParent; + /** the closest common parent of p1 and p2 */ + nsCOMPtr mCommonParent; + /** p2 offset */ PRInt32 mEndOffset; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/base/DeleteTextTxn.cpp b/mozilla/editor/base/DeleteTextTxn.cpp index 09df4b2b0ab..eda056e3a3a 100644 --- a/mozilla/editor/base/DeleteTextTxn.cpp +++ b/mozilla/editor/base/DeleteTextTxn.cpp @@ -17,20 +17,23 @@ */ #include "DeleteTextTxn.h" -#include "editor.h" #include "nsIDOMCharacterData.h" -// note that aEditor is not refcounted -DeleteTextTxn::DeleteTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, + +DeleteTextTxn::DeleteTextTxn() + : EditTxn() +{ +} + +nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aNumCharsToDelete) - : EditTxn(aEditor) { mElement = aElement; mOffset = aOffset; mNumCharsToDelete = aNumCharsToDelete; mDeletedText = ""; + return NS_OK; } nsresult DeleteTextTxn::Do(void) diff --git a/mozilla/editor/base/DeleteTextTxn.h b/mozilla/editor/base/DeleteTextTxn.h index 73d2e35ae08..ad2fd0de334 100644 --- a/mozilla/editor/base/DeleteTextTxn.h +++ b/mozilla/editor/base/DeleteTextTxn.h @@ -20,8 +20,13 @@ #define DeleteTextTxn_h__ #include "EditTxn.h" +#include "nsIDOMCharacterData.h" +#include "nsCOMPtr.h" -class nsIDOMCharacterData; +#define DELETE_TEXT_TXN_IID \ +{/* 4d3a2720-ac49-11d2-86d8-000064657374 */ \ +0x4d3a2720, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that changes an attribute of a content node. @@ -31,11 +36,15 @@ class DeleteTextTxn : public EditTxn { public: - DeleteTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, - PRUint32 aOffset, - PRUint32 aNumCharsToDelete); + virtual nsresult Init(nsIDOMCharacterData *aElement, + PRUint32 aOffset, + PRUint32 aNumCharsToDelete); +private: + DeleteTextTxn(); + +public: + virtual nsresult Do(void); virtual nsresult Undo(void); @@ -53,7 +62,7 @@ public: protected: /** the text element to operate upon */ - nsIDOMCharacterData *mElement; + nsCOMPtr mElement; /** the offset into mElement where the deletion is to take place */ PRUint32 mOffset; @@ -64,6 +73,8 @@ protected: /** the text that was deleted */ nsString mDeletedText; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/base/EditAggregateTxn.cpp b/mozilla/editor/base/EditAggregateTxn.cpp new file mode 100644 index 00000000000..e07c6d3f8c6 --- /dev/null +++ b/mozilla/editor/base/EditAggregateTxn.cpp @@ -0,0 +1,145 @@ +/* -*- 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 "EditAggregateTxn.h" +#include "nsCOMPtr.h" +#include "nsIDOMNode.h" +#include "nsVoidArray.h" + +EditAggregateTxn::EditAggregateTxn() + : EditTxn() +{ + mChildren = new nsVoidArray(); +} + +EditAggregateTxn::~EditAggregateTxn() +{ + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + NS_IF_RELEASE(txn); + } + delete mChildren; + } +} + +nsresult EditAggregateTxn::Do(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + result = txn->Do(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::Undo(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + // undo goes through children backwards + for (i=count-1; i>=0; i--) + { + EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i)); + result = txn->Undo(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::Redo(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + result = txn->Redo(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::GetIsTransient(PRBool *aIsTransient) +{ + if (nsnull!=aIsTransient) + *aIsTransient = PR_TRUE; + return NS_OK; +} + +nsresult EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) +{ + return NS_OK; +} + +nsresult EditAggregateTxn::Write(nsIOutputStream *aOutputStream) +{ + return NS_OK; +} + +nsresult EditAggregateTxn::GetUndoString(nsString **aString) +{ + if (nsnull!=aString) + *aString=nsnull; + return NS_OK; +} + +nsresult EditAggregateTxn::GetRedoString(nsString **aString) +{ + if (nsnull!=aString) + *aString=nsnull; + return NS_OK; +} + +nsresult EditAggregateTxn::AppendChild(EditTxn *aTxn) +{ + if ((nsnull!=mChildren) && (nsnull!=aTxn)) + { + mChildren->AppendElement(aTxn); + return NS_OK; + } + return NS_ERROR_NULL_POINTER; +} + + + + + + diff --git a/mozilla/editor/base/EditAggregateTxn.h b/mozilla/editor/base/EditAggregateTxn.h new file mode 100644 index 00000000000..9830a1d0656 --- /dev/null +++ b/mozilla/editor/base/EditAggregateTxn.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 EditAggregateTxn_h__ +#define EditAggregateTxn_h__ + +#include "EditTxn.h" + +#define EDIT_AGGREGATE_TXN_IID \ +{/* 345921a0-ac49-11d2-86d8-000064657374 */ \ +0x345921a0, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + +class nsVoidArray; + +/** + * base class for all document editing transactions that require aggregation. + * provides a list of child transactions. + */ +class EditAggregateTxn : public EditTxn +{ +public: + + EditAggregateTxn(); + + virtual ~EditAggregateTxn(); + + virtual nsresult Do(void); + + virtual nsresult Undo(void); + + virtual nsresult Redo(void); + + virtual nsresult GetIsTransient(PRBool *aIsTransient); + + virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction); + + virtual nsresult Write(nsIOutputStream *aOutputStream); + + virtual nsresult GetUndoString(nsString **aString); + + virtual nsresult GetRedoString(nsString **aString); + + virtual nsresult AppendChild(EditTxn *aTxn); + +protected: + + nsVoidArray *mChildren; + +}; + +#endif \ No newline at end of file diff --git a/mozilla/editor/base/EditTxn.cpp b/mozilla/editor/base/EditTxn.cpp index 768bef6604e..d17b6d2583f 100644 --- a/mozilla/editor/base/EditTxn.cpp +++ b/mozilla/editor/base/EditTxn.cpp @@ -28,10 +28,8 @@ NS_IMPL_ADDREF(EditTxn) NS_IMPL_RELEASE(EditTxn) // note that aEditor is not refcounted -EditTxn::EditTxn(nsEditor *aEditor) +EditTxn::EditTxn() { - NS_ASSERTION(nsnull!=aEditor, "null aEditor arg to EditTxn constructor"); - mEditor = aEditor; } nsresult EditTxn::Do(void) diff --git a/mozilla/editor/base/EditTxn.h b/mozilla/editor/base/EditTxn.h index afd99fbed46..97381e64dc5 100644 --- a/mozilla/editor/base/EditTxn.h +++ b/mozilla/editor/base/EditTxn.h @@ -20,13 +20,17 @@ #define EditTxn_h__ #include "nsITransaction.h" -class nsEditor; class nsIDOMNode; +#define EDIT_TXN_IID \ +{/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \ +0xc5ea31b0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + /** * base class for all document editing transactions. - * provides access to the nsEditor that created this transaction. + * provides default concrete behavior for all nsITransaction methods. */ class EditTxn : public nsITransaction { @@ -34,7 +38,7 @@ public: NS_DECL_ISUPPORTS - EditTxn(nsEditor *aEditor); + EditTxn(); virtual nsresult Do(void); @@ -52,10 +56,6 @@ public: virtual nsresult GetRedoString(nsString **aString); -protected: - - nsEditor *mEditor; - }; #endif \ No newline at end of file diff --git a/mozilla/editor/base/InsertTextTxn.cpp b/mozilla/editor/base/InsertTextTxn.cpp index 4d078cc193f..220c9e46916 100644 --- a/mozilla/editor/base/InsertTextTxn.cpp +++ b/mozilla/editor/base/InsertTextTxn.cpp @@ -20,18 +20,22 @@ #include "editor.h" #include "nsIDOMCharacterData.h" -static NS_DEFINE_IID(kInsertTextTxnIID, INSERTTEXTTXN_IID); +static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID); -// note that aEditor is not refcounted -InsertTextTxn::InsertTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, + +InsertTextTxn::InsertTextTxn() + : EditTxn() +{ +} + +nsresult InsertTextTxn::Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, const nsString& aStringToInsert) - : EditTxn(aEditor) { mElement = aElement; mOffset = aOffset; mStringToInsert = aStringToInsert; + return NS_OK; } nsresult InsertTextTxn::Do(void) @@ -60,11 +64,11 @@ nsresult InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) if ((nsnull!=aDidMerge) && (nsnull!=aTransaction)) { // if aTransaction isa InsertTextTxn, absorb it - nsCOMPtr otherTxn; - nsresult result = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); + nsCOMPtr otherTxn = aTransaction; + nsresult result=NS_OK;// = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); if (NS_SUCCEEDED(result) && (otherTxn)) { - nsString otherData; + nsAutoString otherData; otherTxn->GetData(otherData); mStringToInsert += otherData; } diff --git a/mozilla/editor/base/InsertTextTxn.h b/mozilla/editor/base/InsertTextTxn.h index 1e7288bb88d..134cddb790b 100644 --- a/mozilla/editor/base/InsertTextTxn.h +++ b/mozilla/editor/base/InsertTextTxn.h @@ -20,14 +20,14 @@ #define InsertTextTxn_h__ #include "EditTxn.h" +#include "nsIDOMCharacterData.h" +#include "nsCOMPtr.h" -#define INSERTTEXTTXN_IID \ +#define INSERT_TEXT_TXN_IID \ {/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \ 0x93276f00, 0xab2c, 0x11d2, \ {0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} } -class nsIDOMCharacterData; - /** * A transaction that changes an attribute of a content node. * This transaction covers add, remove, and change attribute. @@ -36,15 +36,13 @@ class InsertTextTxn : public EditTxn { public: - InsertTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert); + virtual nsresult Init(nsIDOMCharacterData *aElement, + PRUint32 aOffset, + const nsString& aStringToInsert); private: - // default ctor so that nsCOMPtr is happy - InsertTextTxn() : EditTxn(nsnull) {} + InsertTextTxn(); public: @@ -67,7 +65,7 @@ public: // override QueryInterface to handle InsertTextTxn request NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); - static const nsIID& IID() { static nsIID iid = INSERTTEXTTXN_IID; return iid; } + static const nsIID& IID() { static nsIID iid = INSERT_TEXT_TXN_IID; return iid; } virtual nsresult GetData(nsString& aResult); @@ -75,7 +73,7 @@ public: protected: /** the text element to operate upon */ - nsIDOMCharacterData *mElement; + nsCOMPtr mElement; /** the offset into mElement where the insertion is to take place */ PRUint32 mOffset; @@ -86,6 +84,8 @@ protected: /** the text to insert into mElement at mOffset */ nsString mStringToInsert; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/base/JoinElementTxn.cpp b/mozilla/editor/base/JoinElementTxn.cpp new file mode 100644 index 00000000000..e6331ffddf9 --- /dev/null +++ b/mozilla/editor/base/JoinElementTxn.cpp @@ -0,0 +1,122 @@ +/* -*- 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 "JoinElementTxn.h" +#include "nsIDOMNodeList.h" +#include "editor.h" + + +JoinElementTxn::JoinElementTxn() + : EditTxn() +{ +} + +nsresult JoinElementTxn::Init(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode) +{ + mLeftNode = aLeftNode; + mRightNode = aRightNode; + mOffset=0; + return NS_OK; +} + +JoinElementTxn::~JoinElementTxn() +{ +} + +nsresult JoinElementTxn::Do(void) +{ + nsresult result; + + if ((mLeftNode) && (mRightNode)) + { // get the parent node + nsCOMPtrleftParent; + result = mLeftNode->GetParentNode(getter_AddRefs(leftParent)); + if ((NS_SUCCEEDED(result)) && (leftParent)) + { // verify that mLeftNode and mRightNode have the same parent + nsCOMPtrrightParent; + result = mRightNode->GetParentNode(getter_AddRefs(rightParent)); + if ((NS_SUCCEEDED(result)) && (rightParent)) + { + if (leftParent==rightParent) + { + mParent=leftParent; // set this instance mParent. + // Other methods see a non-null mParent and know all is well + nsCOMPtr childNodes; + result = mLeftNode->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(result)) && (childNodes)) + { + childNodes->GetLength(&mOffset); + } + result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE); + } + } + } + } + return result; +} + + +nsresult JoinElementTxn::Undo(void) +{ + nsresult result = nsEditor::SplitNode(mRightNode, mOffset, mLeftNode, mParent); + return result; +} + +nsresult JoinElementTxn::Redo(void) +{ + nsresult result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE); + return result; +} + +nsresult JoinElementTxn::GetIsTransient(PRBool *aIsTransient) +{ + if (nsnull!=aIsTransient) + *aIsTransient = PR_FALSE; + return NS_OK; +} + +nsresult JoinElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) +{ + if (nsnull!=aDidMerge) + *aDidMerge=PR_FALSE; + return NS_OK; +} + +nsresult JoinElementTxn::Write(nsIOutputStream *aOutputStream) +{ + return NS_OK; +} + +nsresult JoinElementTxn::GetUndoString(nsString **aString) +{ + if (nsnull!=aString) + { + **aString="Join Element"; + } + return NS_OK; +} + +nsresult JoinElementTxn::GetRedoString(nsString **aString) +{ + if (nsnull!=aString) + { + **aString="Split Element"; + } + return NS_OK; +} diff --git a/mozilla/editor/base/JoinElementTxn.h b/mozilla/editor/base/JoinElementTxn.h new file mode 100644 index 00000000000..d094efe5f09 --- /dev/null +++ b/mozilla/editor/base/JoinElementTxn.h @@ -0,0 +1,86 @@ +/* -*- 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 JoinElementTxn_h__ +#define JoinElementTxn_h__ + +#include "EditTxn.h" +#include "nsIDOMNode.h" +#include "nsCOMPtr.h" + +#define JOIN_ELEMENT_TXN_IID \ +{/* 9bc5f9f0-ac48-11d2-86d8-000064657374 */ \ +0x9bc5f9f0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + + +/** + * A transaction that joins two elements E1 and E2 into a single node E. + * The children of E are the children of E1 followed by the children of E2. + */ +class JoinElementTxn : public EditTxn +{ +public: + + virtual nsresult Init(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode); +protected: + JoinElementTxn(); + +public: + + virtual ~JoinElementTxn(); + + virtual nsresult Do(void); + + virtual nsresult Undo(void); + + virtual nsresult Redo(void); + + virtual nsresult GetIsTransient(PRBool *aIsTransient); + + virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction); + + virtual nsresult Write(nsIOutputStream *aOutputStream); + + virtual nsresult GetUndoString(nsString **aString); + + virtual nsresult GetRedoString(nsString **aString); + +protected: + + /** the elements to operate upon. + * After the merge, mRightNode remains and mLeftNode is removed from the content tree. + */ + nsCOMPtr mLeftNode; + nsCOMPtr mRightNode; + + /** the offset into mNode where the children of mElement are split (for undo).
+ * mOffset is the index of the last child in the left node. + * -1 means the left node gets no children. + */ + PRUint32 mOffset; + + /** the parent node containing mLeftNode and mRightNode */ + nsCOMPtr mParent; + + friend class TransactionFactory; + +}; + +#endif diff --git a/mozilla/editor/base/Makefile.in b/mozilla/editor/base/Makefile.in index e962b20f862..a962248fc9e 100644 --- a/mozilla/editor/base/Makefile.in +++ b/mozilla/editor/base/Makefile.in @@ -30,12 +30,15 @@ CPPSRCS = \ nsEditFactory.cpp \ ChangeAttributeTxn.cpp \ EditTxn.cpp \ + EditAggregateTxn.cpp \ InsertTextTxn.cpp \ DeleteTextTxn.cpp \ CreateElementTxn.cpp \ DeleteElementTxn.cpp \ DeleteRangeTxn.cpp \ SplitElementTxn.cpp \ + JoinElementTxn.cpp \ + TransactionFactory.cpp \ $(NULL) MODULE = editor diff --git a/mozilla/editor/base/SplitElementTxn.cpp b/mozilla/editor/base/SplitElementTxn.cpp index 2134cc5375c..dcabdec1865 100644 --- a/mozilla/editor/base/SplitElementTxn.cpp +++ b/mozilla/editor/base/SplitElementTxn.cpp @@ -17,44 +17,42 @@ */ #include "SplitElementTxn.h" -#include "editor.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" +#include "editor.h" // note that aEditor is not refcounted -SplitElementTxn::SplitElementTxn(nsEditor *aEditor, - nsIDOMNode *aNode, - PRInt32 aOffset) - : EditTxn(aEditor) +SplitElementTxn::SplitElementTxn() + : EditTxn() { - mNode = aNode; - NS_ADDREF(mNode); +} + +nsresult SplitElementTxn::Init(nsIDOMNode *aNode, + PRInt32 aOffset) +{ + mExistingRightNode = aNode; mOffset = aOffset; - mNewNode = nsnull; - mParent = nsnull; + return NS_OK; } SplitElementTxn::~SplitElementTxn() { - NS_IF_RELEASE(mNode); - NS_IF_RELEASE(mNewNode); - NS_IF_RELEASE(mParent); } nsresult SplitElementTxn::Do(void) { // create a new node - nsresult result = mNode->CloneNode(PR_FALSE, &mNewNode); - NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); + nsresult result = mExistingRightNode->CloneNode(PR_FALSE, getter_AddRefs(mNewLeftNode)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element."); - if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) + if ((NS_SUCCEEDED(result)) && (mNewLeftNode)) { // get the parent node - result = mNode->GetParentNode(&mParent); + result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent)); // insert the new node - if ((NS_SUCCEEDED(result)) && (nsnull!=mParent)) + if ((NS_SUCCEEDED(result)) && (mParent)) { - result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); + result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent); } } return result; @@ -63,13 +61,13 @@ nsresult SplitElementTxn::Do(void) nsresult SplitElementTxn::Undo(void) { // this assumes Do inserted the new node in front of the prior existing node - nsresult result = mEditor->JoinNodes(mNode, mNewNode, mParent, PR_FALSE); + nsresult result = nsEditor::JoinNodes(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); return result; } nsresult SplitElementTxn::Redo(void) { - nsresult result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); + nsresult result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent); return result; } diff --git a/mozilla/editor/base/SplitElementTxn.h b/mozilla/editor/base/SplitElementTxn.h index 8fb19df1fb6..051021e6bb8 100644 --- a/mozilla/editor/base/SplitElementTxn.h +++ b/mozilla/editor/base/SplitElementTxn.h @@ -20,10 +20,13 @@ #define SplitElementTxn_h__ #include "EditTxn.h" +#include "nsIDOMNode.h" +#include "nsCOMPtr.h" -class nsIDOMNode; -class nsIDOMElement; -class nsIDOMDocument; +#define SPLIT_ELEMENT_TXN_IID \ +{/* 690c6290-ac48-11d2-86d8-000064657374 */ \ +0x690c6290, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that splits an element E into two identical nodes, E1 and E2 @@ -33,10 +36,12 @@ class SplitElementTxn : public EditTxn { public: - SplitElementTxn(nsEditor *aEditor, - nsIDOMNode *aNode, - PRInt32 aOffset); + virtual nsresult Init (nsIDOMNode *aNode, + PRInt32 aOffset); +protected: + SplitElementTxn(); +public: virtual ~SplitElementTxn(); virtual nsresult Do(void); @@ -58,7 +63,7 @@ public: protected: /** the element to operate upon */ - nsIDOMNode *mNode; + nsCOMPtr mExistingRightNode; /** the offset into mElement where the children of mElement are split.
* mOffset is the index of the last child in the left node. @@ -67,8 +72,12 @@ protected: PRInt32 mOffset; /** the element we create when splitting mElement */ - nsIDOMNode *mNewNode; - nsIDOMNode *mParent; + nsCOMPtr mNewLeftNode; + + /** the parent shared by mExistingRightNode and mNewLeftNode */ + nsCOMPtr mParent; + + friend class TransactionFactory; }; diff --git a/mozilla/editor/base/TransactionFactory.cpp b/mozilla/editor/base/TransactionFactory.cpp new file mode 100644 index 00000000000..cc4be1af14f --- /dev/null +++ b/mozilla/editor/base/TransactionFactory.cpp @@ -0,0 +1,75 @@ +/* -*- 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 "TransactionFactory.h" +// transactions this factory knows how to build +#include "InsertTextTxn.h" +#include "DeleteTextTxn.h" +#include "CreateElementTxn.h" +#include "DeleteElementTxn.h" +#include "DeleteRangeTxn.h" +#include "ChangeAttributeTxn.h" +#include "SplitElementTxn.h" +#include "JoinElementTxn.h" + +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); +static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID); +static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID); +static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID); + +TransactionFactory::TransactionFactory() +{ +} + +TransactionFactory::~TransactionFactory() +{ +} + +nsresult +TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult) +{ + nsresult result = NS_OK; + *aResult = nsnull; + if (aTxnType.Equals(kInsertTextTxnIID)) + *aResult = new InsertTextTxn(); + else if (aTxnType.Equals(kDeleteTextTxnIID)) + *aResult = new DeleteTextTxn(); + else if (aTxnType.Equals(kCreateElementTxnIID)) + *aResult = new CreateElementTxn(); + else if (aTxnType.Equals(kDeleteElementTxnIID)) + *aResult = new DeleteElementTxn(); + else if (aTxnType.Equals(kDeleteRangeTxnIID)) + *aResult = new DeleteRangeTxn(); + else if (aTxnType.Equals(kChangeAttributeTxnIID)) + *aResult = new ChangeAttributeTxn(); + else if (aTxnType.Equals(kSplitElementTxnIID)) + *aResult = new SplitElementTxn(); + else if (aTxnType.Equals(kJoinElementTxnIID)) + *aResult = new JoinElementTxn(); + + if (nsnull==*aResult) + result = NS_ERROR_INVALID_ARG; + + return result; +} + + diff --git a/mozilla/editor/base/TransactionFactory.h b/mozilla/editor/base/TransactionFactory.h new file mode 100644 index 00000000000..58276d5679f --- /dev/null +++ b/mozilla/editor/base/TransactionFactory.h @@ -0,0 +1,41 @@ +/* -*- 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 TransactionFactory_h__ +#define TransactionFactory_h__ + +#include "nsISupports.h" + +class EditTxn; + +/** + * This class instantiates and optionally recycles edit transactions + * A recycler would be a separate static object, since this class does not get instantiated + */ +class TransactionFactory +{ +protected: + TransactionFactory(); + virtual ~TransactionFactory(); + +public: + static nsresult GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult); + +}; + +#endif diff --git a/mozilla/editor/base/editor.cpp b/mozilla/editor/base/editor.cpp index 0eeeee4a365..ad002f187fa 100644 --- a/mozilla/editor/base/editor.cpp +++ b/mozilla/editor/base/editor.cpp @@ -33,16 +33,22 @@ #include "nsTransactionManagerCID.h" #include "nsITransactionManager.h" #include "nsIPresShell.h" +#include "nsIViewManager.h" #include "nsISelection.h" +#include "nsICollection.h" +#include "nsIEnumerator.h" #include "nsIAtom.h" // transactions the editor knows how to build +#include "TransactionFactory.h" #include "ChangeAttributeTxn.h" #include "CreateElementTxn.h" #include "DeleteElementTxn.h" #include "InsertTextTxn.h" #include "DeleteTextTxn.h" #include "DeleteRangeTxn.h" +#include "SplitElementTxn.h" +#include "JoinElementTxn.h" static NS_DEFINE_IID(kIDOMEventReceiverIID, NS_IDOMEVENTRECEIVER_IID); @@ -58,8 +64,18 @@ static NS_DEFINE_IID(kIEditFactoryIID, NS_IEDITORFACTORY_IID); static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID); static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kEditorCID, NS_EDITOR_CID); +// transaction manager static NS_DEFINE_IID(kITransactionManagerIID, NS_ITRANSACTIONMANAGER_IID); static NS_DEFINE_CID(kCTransactionManagerFactoryCID, NS_TRANSACTION_MANAGER_FACTORY_CID); +// transactions +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); +static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID); +static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID); +static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID); #ifdef XP_PC @@ -165,6 +181,7 @@ nsEditor::nsEditor() nsEditor::~nsEditor() { NS_IF_RELEASE(mPresShell); + NS_IF_RELEASE(mViewManager); NS_IF_RELEASE(mTxnMgr); //the autopointers will clear themselves up. @@ -229,6 +246,8 @@ nsEditor::Init(nsIDOMDocument *aDomInterface, nsIPresShell* aPresShell) mDomInterfaceP = aDomInterface; mPresShell = aPresShell; NS_ADDREF(mPresShell); + mViewManager = mPresShell->GetViewManager(); + mUpdateCount=0; nsresult t_result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this); if (NS_OK != t_result) @@ -308,13 +327,13 @@ nsEditor::Init(nsIDOMDocument *aDomInterface, nsIPresShell* aPresShell) nsresult nsEditor::InsertString(nsString *aString) { - return AppendText(aString); + return NS_OK; } nsresult -nsEditor::SetProperties(PROPERTIES aProperty) +nsEditor::SetProperties(Properties aProperties) { return NS_OK; } @@ -322,7 +341,7 @@ nsEditor::SetProperties(PROPERTIES aProperty) nsresult -nsEditor::GetProperties(PROPERTIES **) +nsEditor::GetProperties(Properties **aProperties) { return NS_OK; } @@ -334,9 +353,13 @@ nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, cons nsresult result; if (nsnull != aElement) { - ChangeAttributeTxn *txn = new ChangeAttributeTxn(this, aElement, aAttribute, aValue, PR_FALSE); + ChangeAttributeTxn *txn; + result = TransactionFactory::GetNewTransaction(kChangeAttributeTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + txn->Init(this, aElement, aAttribute, aValue, PR_FALSE); + result = Do(txn); + } } return result; } @@ -369,10 +392,14 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute) nsresult result; if (nsnull != aElement) { - nsString value; - ChangeAttributeTxn *txn = new ChangeAttributeTxn(this, aElement, aAttribute, value, PR_TRUE); + ChangeAttributeTxn *txn; + result = TransactionFactory::GetNewTransaction(kChangeAttributeTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + nsAutoString value; + txn->Init(this, aElement, aAttribute, value, PR_TRUE); + result = Do(txn); + } } return result; } @@ -420,26 +447,6 @@ nsEditor::MouseClick(int aX,int aY) //BEGIN nsEditor Private methods - -nsresult -nsEditor::AppendText(nsString *aStr) -{ - nsCOMPtr currentNode; - nsCOMPtr textNode; - nsCOMPtr text; - if (!aStr) - return NS_ERROR_NULL_POINTER; - if (NS_SUCCEEDED(GetCurrentNode(getter_AddRefs(currentNode))) && - NS_SUCCEEDED(GetFirstTextNode(currentNode,getter_AddRefs(textNode))) && - NS_SUCCEEDED(textNode->QueryInterface(kIDOMTextIID, getter_AddRefs(text)))) { - text->AppendData(*aStr); - } - - return NS_OK; -} - - - nsresult nsEditor::GetCurrentNode(nsIDOMNode ** aNode) { @@ -485,7 +492,7 @@ nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDO while (childNode) { result = childNode->QueryInterface(kIDOMNodeIID,getter_AddRefs(element)); - nsString tag; + nsAutoString tag; if (NS_SUCCEEDED(result) && (element)) { element->GetTagName(tag); @@ -500,9 +507,8 @@ nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDO return result; } } - nsCOMPtr xNode; - childNode->GetNextSibling(getter_AddRefs(xNode)); - childNode=xNode; + nsCOMPtr temp = childNode; + temp->GetNextSibling(getter_AddRefs(childNode)); } return NS_ERROR_FAILURE; @@ -559,7 +565,7 @@ nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode) } nsresult -nsEditor::ExecuteTransaction(nsITransaction *aTxn) +nsEditor::Do(nsITransaction *aTxn) { nsresult result = NS_OK; if (nsnull!=aTxn) @@ -577,27 +583,65 @@ nsEditor::ExecuteTransaction(nsITransaction *aTxn) } nsresult -nsEditor::Undo() +nsEditor::Undo(PRUint32 aCount) { nsresult result = NS_OK; if (nsnull!=mTxnMgr) { - result = mTxnMgr->Undo(); + PRUint32 i=0; + for ( ; iUndo(); + if (NS_FAILED(result)) + break; + } } return result; } nsresult -nsEditor::Redo() +nsEditor::Redo(PRUint32 aCount) { nsresult result = NS_OK; if (nsnull!=mTxnMgr) { - result = mTxnMgr->Redo(); + PRUint32 i=0; + for ( ; iRedo(); + if (NS_FAILED(result)) + break; + } } return result; } +nsresult +nsEditor::BeginUpdate() +{ + NS_PRECONDITION(mUpdateCount>=0, "bad state"); + if (nsnull!=mViewManager) + { + if (0==mUpdateCount) + mViewManager->DisableRefresh(); + mUpdateCount++; + } + return NS_OK; +} + +nsresult +nsEditor::EndUpdate() +{ + NS_PRECONDITION(mUpdateCount>0, "bad state"); + if (nsnull!=mViewManager) + { + mUpdateCount--; + if (0==mUpdateCount) + mViewManager->EnableRefresh(); + } + return NS_OK; +} + nsresult nsEditor::Delete(PRBool aForward, PRUint32 aCount) { return NS_OK; @@ -610,9 +654,13 @@ nsresult nsEditor::CreateElement(const nsString& aTag, nsresult result; if (nsnull != aParent) { - CreateElementTxn *txn = new CreateElementTxn(this, mDomInterfaceP, aTag, aParent, aPosition); + CreateElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kCreateElementTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + txn->Init(mDomInterfaceP, aTag, aParent, aPosition); + result = Do(txn); + } else result = NS_ERROR_OUT_OF_MEMORY; } @@ -628,9 +676,13 @@ nsresult nsEditor::DeleteElement(nsIDOMNode * aParent, nsresult result; if ((nsnull != aParent) && (nsnull != aElement)) { - DeleteElementTxn *txn = new DeleteElementTxn(this, mDomInterfaceP, aElement, aParent); + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + txn->Init(aElement, aParent); + result = Do(txn); + } else result = NS_ERROR_OUT_OF_MEMORY; } @@ -640,18 +692,51 @@ nsresult nsEditor::DeleteElement(nsIDOMNode * aParent, return result; } -nsresult nsEditor::InsertText(nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert) +// XXX; factor -- should call BuildInsertTextTransaction to do most of the work +nsresult nsEditor::InsertText(const nsString& aStringToInsert) { nsresult result; - if (nsnull != aElement) + nsISelection* selection; + result = mPresShell->GetSelection(&selection); + if ((NS_SUCCEEDED(result)) && (nsnull!=selection)) { - InsertTextTxn *txn = new InsertTextTxn(this, aElement, aOffset, aStringToInsert); - if (nsnull!=txn) - result = ExecuteTransaction(txn); - else - result = NS_ERROR_OUT_OF_MEMORY; + nsCOMPtr enumerator; + enumerator = selection; + if (enumerator) + { + enumerator->First(); + nsISupports *currentItem; + result = enumerator->CurrentItem(¤tItem); + if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) + { + // XXX: we'll want to deleteRange if the selection isn't just an insertion point + // for now, just insert text after the start of the first node + nsCOMPtr range = currentItem; + if (range) + { + nsCOMPtr node; + result = range->GetStartParent(getter_AddRefs(node)); + if ((NS_SUCCEEDED(result)) && (node)) + { + nsCOMPtr nodeAsText = node; + if (nodeAsText) + { + PRInt32 offset; + range->GetStartOffset(&offset); + InsertTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kInsertTextTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(nodeAsText, offset, aStringToInsert); + result = Do(txn); + } + else + result = NS_ERROR_OUT_OF_MEMORY; + } + } + } + } + } } else result = NS_ERROR_INVALID_ARG; @@ -659,6 +744,7 @@ nsresult nsEditor::InsertText(nsIDOMCharacterData *aElement, return result; } +// XXX; factor -- should call BuildDeleteTextTransaction to do most of the work nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aLength) @@ -666,9 +752,13 @@ nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement, nsresult result=NS_OK; if (nsnull != aElement) { - DeleteTextTxn *txn = new DeleteTextTxn(this, aElement, aOffset, aLength); + DeleteTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + txn->Init(aElement, aOffset, aLength); + result = Do(txn); + } else result = NS_ERROR_OUT_OF_MEMORY; } @@ -678,6 +768,10 @@ nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement, return result; } +// XXX; factor -- should call DeleteSelectionTransaction to do most of the work +// XXX: these should get wrapped up in a single composite transaction +// rather than executing each individually, maybe I should alloc a generic aggregate +// and stick each in there, then execute the aggregate nsresult nsEditor::DeleteSelection() { nsresult result; @@ -685,15 +779,34 @@ nsresult nsEditor::DeleteSelection() result = mPresShell->GetSelection(&selection); if ((NS_SUCCEEDED(result)) && (nsnull!=selection)) { - nsCOMPtr range; - result = selection->QueryInterface(kIDOMRangeIID, getter_AddRefs(range)); - if ((NS_SUCCEEDED(result)) && (range)) + nsCOMPtr enumerator; + enumerator = selection; + if (enumerator) { - DeleteRangeTxn *txn = new DeleteRangeTxn(this, range); - if (nsnull!=txn) - result = ExecuteTransaction(txn); - else - result = NS_ERROR_OUT_OF_MEMORY; + enumerator->First(); + nsISupports *currentItem; + result = enumerator->CurrentItem(¤tItem); + if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) + { + nsCOMPtr range = currentItem; + DeleteRangeTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteRangeTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(range); + result = Do(txn); + } + else + result = NS_ERROR_OUT_OF_MEMORY; + if (NS_SUCCEEDED(result)) + { + nsresult nextResult = enumerator->Next(); + if (NS_SUCCEEDED(nextResult)) + { + result = enumerator->CurrentItem(¤tItem); + } + } + } } } else @@ -703,30 +816,31 @@ nsresult nsEditor::DeleteSelection() } nsresult -nsEditor::SplitNode(nsIDOMNode * aNode, +nsEditor::SplitNode(nsIDOMNode * aExistingRightNode, PRInt32 aOffset, - nsIDOMNode* aNewNode, + nsIDOMNode* aNewLeftNode, nsIDOMNode* aParent) { nsresult result; - NS_ASSERTION(((nsnull!=aNode) && - (nsnull!=aNewNode) && + NS_ASSERTION(((nsnull!=aExistingRightNode) && + (nsnull!=aNewLeftNode) && (nsnull!=aParent)), "null arg"); - if ((nsnull!=aNode) && - (nsnull!=aNewNode) && + if ((nsnull!=aExistingRightNode) && + (nsnull!=aNewLeftNode) && (nsnull!=aParent)) { nsCOMPtr resultNode; - result = aParent->InsertBefore(aNewNode, aNode, getter_AddRefs(resultNode)); + result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode)); if (NS_SUCCEEDED(result)) { // split the children between the 2 nodes - // at this point, nNode has all the children + // at this point, aExistingRightNode has all the children + // move all the children whose index is < aOffset to aNewLeftNode if (0<=aOffset) // don't bother unless we're going to move at least one child { nsCOMPtr childNodes; - result = aParent->GetChildNodes(getter_AddRefs(childNodes)); + result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { PRInt32 i=0; @@ -736,10 +850,10 @@ nsEditor::SplitNode(nsIDOMNode * aNode, result = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(result)) && (childNode)) { - result = aNode->RemoveChild(childNode, getter_AddRefs(resultNode)); + result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode)); if (NS_SUCCEEDED(result)) { - result = aNewNode->AppendChild(childNode, getter_AddRefs(resultNode)); + result = aNewLeftNode->AppendChild(childNode, getter_AddRefs(resultNode)); } } } diff --git a/mozilla/editor/base/editor.h b/mozilla/editor/base/editor.h index 243f9339873..7a2b7790b7f 100644 --- a/mozilla/editor/base/editor.h +++ b/mozilla/editor/base/editor.h @@ -16,6 +16,9 @@ * Reserved. */ +#ifndef __editor_h__ +#define __editor_h__ + #include "prmon.h" #include "nsIEditor.h" #include "nsIContextLoader.h" @@ -24,11 +27,13 @@ #include "nsCOMPtr.h" #include "editorInterfaces.h" #include "nsITransactionManager.h" +#include "TransactionFactory.h" #include "nsRepository.h" //#include "nsISelection.h" class nsIDOMCharacterData; class nsIPresShell; +class nsIViewManager; //This is the monitor for the editor. PRMonitor *getEditorMonitor(); @@ -42,15 +47,19 @@ PRMonitor *getEditorMonitor(); class nsEditor : public nsIEditor { private: - nsIPresShell *mPresShell; + nsIPresShell *mPresShell; + nsIViewManager *mViewManager; + PRUint32 mUpdateCount; nsCOMPtr mDomInterfaceP; nsCOMPtr mKeyListenerP; nsCOMPtr mMouseListenerP; // nsCOMPtr mSelectionP; //nsCOMPtr mTxnMgrP; + nsITransactionManager * mTxnMgr; friend PRBool NSCanUnload(void); static PRInt32 gInstanceCount; + public: /** The default constructor. This should suffice. the setting of the interfaces is done * after the construction of the editor class. @@ -70,9 +79,9 @@ public: virtual nsresult GetDomInterface(nsIDOMDocument **aDomInterface); - virtual nsresult SetProperties(PROPERTIES aProperty); + virtual nsresult SetProperties(Properties aProperties); - virtual nsresult GetProperties(PROPERTIES **); + virtual nsresult GetProperties(Properties **aProperties); virtual nsresult SetAttribute(nsIDOMElement * aElement, const nsString& aAttribute, @@ -85,11 +94,15 @@ public: virtual nsresult RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute); - virtual nsresult InsertString(nsString *aString); - - virtual nsresult Commit(PRBool aCtrlKey); + virtual nsresult Do(nsITransaction *aTxn); + virtual nsresult Undo(PRUint32 aCount); + virtual nsresult Redo(PRUint32 aCount); + + virtual nsresult BeginUpdate(); + + virtual nsresult EndUpdate(); /*END nsIEditor interfaces*/ @@ -113,12 +126,6 @@ public: /*BEGIN private methods used by the implementations of the above functions*/ - /** AppendText is a private method that accepts a pointer to a string - * and will append it to the current node. this will be depricated - * @param nsString *aStr is the pointer to the valid string - */ - nsresult AppendText(nsString *aStr); - /** GetCurrentNode ADDREFFS and will get the current node from selection. * now it simply returns the first node in the dom * @param nsIDOMNode **aNode is the return location of the dom node @@ -142,33 +149,6 @@ public: */ nsresult GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDOMNode **aResult); - /** ExecuteTransaction fires a transaction. 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 used. - * Otherwise, the transaction is just executed directly. - * - * @param aTxn the transaction to execute - */ - nsresult ExecuteTransaction(nsITransaction *aTxn); - - /** Undo reverses the effects of the last ExecuteTransaction 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 undo and the result of - * that undo is returned. - * Otherwise, the Undo request is ignored. - * - */ - nsresult Undo(); - - /** 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 - * that redo is returned. - * Otherwise, the Redo request is ignored. - * - */ - nsresult Redo(); - nsresult CreateElement(const nsString& aTag, nsIDOMNode * aParent, PRInt32 aPosition); @@ -178,17 +158,15 @@ public: nsresult DeleteSelection(); - nsresult InsertText(nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert); + nsresult InsertText(const nsString& aStringToInsert); nsresult DeleteText(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aLength); - static nsresult SplitNode(nsIDOMNode * aNode, + static nsresult SplitNode(nsIDOMNode * aExistingRightNode, PRInt32 aOffset, - nsIDOMNode * aNewNode, + nsIDOMNode * aNewLeftNode, nsIDOMNode * aParent); static nsresult JoinNodes(nsIDOMNode * aNodeToKeep, @@ -198,6 +176,10 @@ public: nsresult Delete(PRBool aForward, PRUint32 aCount); + virtual nsresult InsertString(nsString *aString); + + virtual nsresult Commit(PRBool aCtrlKey); + /*END private methods of nsEditor*/ }; @@ -207,3 +189,6 @@ factory method(s) */ nsresult NS_MakeEditorLoader(nsIContextLoader **aResult); + + +#endif \ No newline at end of file diff --git a/mozilla/editor/base/editorInterfaces.cpp b/mozilla/editor/base/editorInterfaces.cpp index a9d5a1abfb9..baebf408a85 100644 --- a/mozilla/editor/base/editorInterfaces.cpp +++ b/mozilla/editor/base/editorInterfaces.cpp @@ -19,7 +19,6 @@ #include "editor.h" #include "CreateElementTxn.h" -#include "SplitElementTxn.h" #include "nsIDOMDocument.h" #include "nsIDOMElement.h" @@ -178,7 +177,7 @@ nsEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) } else { // XXX: delete the first P we find - nsString pTag("P"); + nsAutoString pTag("P"); nsCOMPtr currentNode; nsCOMPtr parentNode; nsCOMPtr element; @@ -199,25 +198,12 @@ nsEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) { // XXX Replace with x-platform NS-virtkeycode transform. if (NS_OK == GetCharFromKeyCode(keyCode, isShift, & character)) { - nsString key; + nsAutoString key; key += character; if (!isShift) { key.ToLowerCase(); } - - // XXX: for now, just grab the first text node - nsCOMPtr currentNode; - nsCOMPtr textNode; - nsCOMPtr text; - if (NS_SUCCEEDED(mEditor->GetCurrentNode(getter_AddRefs(currentNode))) && - NS_SUCCEEDED(mEditor->GetFirstTextNode(currentNode,getter_AddRefs(textNode))) && - NS_SUCCEEDED(textNode->QueryInterface(kIDOMCharacterDataIID, getter_AddRefs(text)))) - { - // XXX: for now, just append the text - PRUint32 offset; - text->GetLength(&offset); - mEditor->InsertText(text, offset, key); - } + mEditor->InsertText(key); } } break; @@ -244,6 +230,10 @@ nsEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) return NS_OK; } +/* these includes are for debug only. this module should never instantiate it's own transactions */ +#include "SplitElementTxn.h" +#include "TransactionFactory.h" +static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID); nsresult nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProcessed) { @@ -265,7 +255,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces if (PR_TRUE==ctrlKey) { if (nsnull!=mEditor) - mEditor->Undo(); + mEditor->Undo(1); } aProcessed=PR_TRUE; break; @@ -275,7 +265,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces if (PR_TRUE==ctrlKey) { if (nsnull!=mEditor) - mEditor->Redo(); + mEditor->Redo(1); } aProcessed=PR_TRUE; break; @@ -283,17 +273,27 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces // hard-coded split node test: works on first

in the document case nsIDOMEvent::VK_S: { - nsString pTag("P"); + nsAutoString pTag("P"); nsCOMPtr currentNode; nsCOMPtr element; if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, pTag, getter_AddRefs(currentNode)))) { + nsresult result; SplitElementTxn *txn; if (PR_FALSE==isShift) // split the element so there are 0 children in the first half - txn = new SplitElementTxn(mEditor, currentNode, -1); + { + result = TransactionFactory::GetNewTransaction(kSplitElementTxnIID, (EditTxn **)&txn); + if (txn) + txn->Init(currentNode, -1); + } else // split the element so there are 2 children in the first half - txn = new SplitElementTxn(mEditor, currentNode, 1); - mEditor->ExecuteTransaction(txn); + { + result = TransactionFactory::GetNewTransaction(kSplitElementTxnIID, (EditTxn **)&txn); + if (txn) + txn->Init(currentNode, 1); + } + if (txn) + mEditor->Do(txn); } } aProcessed=PR_TRUE; @@ -305,10 +305,10 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces { //XXX: should be from a factory //XXX: should manage the refcount of txn - nsString attribute("width"); - nsString value("400"); + nsAutoString attribute("width"); + nsAutoString value("400"); - nsString tableTag("TABLE"); + nsAutoString tableTag("TABLE"); nsCOMPtr currentNode; nsCOMPtr element; if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, tableTag, getter_AddRefs(currentNode)))) @@ -331,11 +331,11 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces nsresult result; //XXX: should be from a factory //XXX: should manage the refcount of txn - nsString attribute("src"); - nsString value("resource:/res/samples/raptor.jpg"); + nsAutoString attribute("src"); + nsAutoString value("resource:/res/samples/raptor.jpg"); - nsString imgTag("HR"); - nsString bodyTag("BODY"); + nsAutoString imgTag("HR"); + nsAutoString bodyTag("BODY"); nsCOMPtr currentNode; result = mEditor->GetFirstNodeOfType(nsnull, bodyTag, getter_AddRefs(currentNode)); if (NS_SUCCEEDED(result)) @@ -356,7 +356,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces { ChangeAttributeTxn *txn; txn = new ChangeAttributeTxn(mEditor, element, attribute, value, PR_FALSE); - mEditor->ExecuteTransaction(txn); + mEditor->Do(txn); } } */ @@ -523,3 +523,4 @@ NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsEditor *a return it->QueryInterface(kIDOMEventListenerIID, (void **) aInstancePtrResult); } + diff --git a/mozilla/editor/base/makefile.win b/mozilla/editor/base/makefile.win index 0eb893bbaf5..cad929eda1f 100644 --- a/mozilla/editor/base/makefile.win +++ b/mozilla/editor/base/makefile.win @@ -25,6 +25,7 @@ CPPSRCS = \ editorInterfaces.cpp \ nsEditFactory.cpp \ EditTxn.cpp \ + EditAggregateTxn.cpp \ ChangeAttributeTxn.cpp \ InsertTextTxn.cpp \ DeleteTextTxn.cpp \ @@ -32,6 +33,8 @@ CPPSRCS = \ DeleteElementTxn.cpp \ DeleteRangeTxn.cpp \ SplitElementTxn.cpp \ + JoinElementTxn.cpp \ + TransactionFactory.cpp \ $(NULL) CPP_OBJS = \ @@ -39,6 +42,7 @@ CPP_OBJS = \ .\$(OBJDIR)\nsEditFactory.obj \ .\$(OBJDIR)\editorInterfaces.obj \ .\$(OBJDIR)\EditTxn.obj \ + .\$(OBJDIR)\EditAggregateTxn.obj \ .\$(OBJDIR)\ChangeAttributeTxn.obj \ .\$(OBJDIR)\InsertTextTxn.obj \ .\$(OBJDIR)\DeleteTextTxn.obj \ @@ -46,6 +50,8 @@ CPP_OBJS = \ .\$(OBJDIR)\DeleteElementTxn.obj \ .\$(OBJDIR)\DeleteRangeTxn.obj \ .\$(OBJDIR)\SplitElementTxn.obj \ + .\$(OBJDIR)\JoinElementTxn.obj \ + .\$(OBJDIR)\TransactionFactory.obj \ $(NULL) MODULE=editor diff --git a/mozilla/editor/core/ChangeAttributeTxn.cpp b/mozilla/editor/core/ChangeAttributeTxn.cpp index 234e9031c84..e3cc0e8fb0b 100644 --- a/mozilla/editor/core/ChangeAttributeTxn.cpp +++ b/mozilla/editor/core/ChangeAttributeTxn.cpp @@ -17,23 +17,33 @@ */ #include "ChangeAttributeTxn.h" -#include "editor.h" #include "nsIDOMElement.h" +#include "editor.h" -// note that aEditor is not refcounted -ChangeAttributeTxn::ChangeAttributeTxn(nsEditor *aEditor, - nsIDOMElement *aElement, - const nsString& aAttribute, - const nsString& aValue, - PRBool aRemoveAttribute) - : EditTxn(aEditor) +ChangeAttributeTxn::ChangeAttributeTxn() + : EditTxn() { - mElement = aElement; - mAttribute = aAttribute; - mValue = aValue; - mRemoveAttribute = aRemoveAttribute; - mAttributeWasSet=PR_FALSE; - mUndoValue=""; +} + +nsresult ChangeAttributeTxn::Init(nsIEditor *aEditor, + nsIDOMElement *aElement, + const nsString& aAttribute, + const nsString& aValue, + PRBool aRemoveAttribute) +{ + if (nsnull!=aEditor && nsnull!=aElement) + { + mEditor = aEditor; + mElement = aElement; + mAttribute = aAttribute; + mValue = aValue; + mRemoveAttribute = aRemoveAttribute; + mAttributeWasSet=PR_FALSE; + mUndoValue=""; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; } nsresult ChangeAttributeTxn::Do(void) diff --git a/mozilla/editor/core/ChangeAttributeTxn.h b/mozilla/editor/core/ChangeAttributeTxn.h index 689d991f3c0..48bded0e116 100644 --- a/mozilla/editor/core/ChangeAttributeTxn.h +++ b/mozilla/editor/core/ChangeAttributeTxn.h @@ -20,8 +20,14 @@ #define ChangeAttributeTxn_h__ #include "EditTxn.h" +#include "nsCOMPtr.h" +#include "nsIDOMElement.h" +#include "nsIEditor.h" -class nsIDOMElement; +#define CHANGE_ATTRIBUTE_TXN_IID \ +{/* 97818860-ac48-11d2-86d8-000064657374 */ \ +0x97818860, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that changes an attribute of a content node. @@ -31,11 +37,16 @@ class ChangeAttributeTxn : public EditTxn { public: - ChangeAttributeTxn(nsEditor *aEditor, - nsIDOMElement *aElement, - const nsString& aAttribute, - const nsString& aValue, - PRBool aRemoveAttribute); + virtual nsresult Init(nsIEditor *aEditor, + nsIDOMElement *aElement, + const nsString& aAttribute, + const nsString& aValue, + PRBool aRemoveAttribute); + +private: + ChangeAttributeTxn(); + +public: virtual nsresult Do(void); @@ -54,9 +65,12 @@ public: virtual nsresult GetRedoString(nsString **aString); protected: + + /** the editor that created this transaction */ + nsCOMPtr mEditor; /** the element to operate upon */ - nsIDOMElement *mElement; + nsCOMPtr mElement; /** the attribute to change */ nsString mAttribute; @@ -72,6 +86,8 @@ protected: /** PR_TRUE if the operation is to remove mAttribute from mElement */ PRBool mRemoveAttribute; + + friend class TransactionFactory; }; #endif diff --git a/mozilla/editor/core/CreateElementTxn.cpp b/mozilla/editor/core/CreateElementTxn.cpp index c62adf8b423..44f1b69170a 100644 --- a/mozilla/editor/core/CreateElementTxn.cpp +++ b/mozilla/editor/core/CreateElementTxn.cpp @@ -17,52 +17,50 @@ */ #include "CreateElementTxn.h" -#include "editor.h" -#include "nsIDOMNode.h" #include "nsIDOMNodeList.h" -#include "nsIDOMDocument.h" -#include "nsIDOMElement.h" -// note that aEditor is not refcounted -CreateElementTxn::CreateElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - const nsString& aTag, - nsIDOMNode *aParent, - PRUint32 aOffsetInParent) - : EditTxn(aEditor) +CreateElementTxn::CreateElementTxn() + : EditTxn() { - mDoc = aDoc; - NS_ADDREF(mDoc); - mTag = aTag; - mParent = aParent; - NS_ADDREF(mParent); - mOffsetInParent = aOffsetInParent; - mNewNode = nsnull; - mRefNode = nsnull; } +nsresult CreateElementTxn::Init(nsIDOMDocument *aDoc, + const nsString& aTag, + nsIDOMNode *aParent, + PRUint32 aOffsetInParent) +{ + if ((nsnull!=aDoc) && (nsnull!=aParent)) + { + mDoc = aDoc; + mTag = aTag; + mParent = aParent; + mOffsetInParent = aOffsetInParent; + mNewNode = nsnull; + mRefNode = nsnull; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; +} + + CreateElementTxn::~CreateElementTxn() { - NS_IF_RELEASE(mDoc); - NS_IF_RELEASE(mParent); - NS_IF_RELEASE(mNewNode); } nsresult CreateElementTxn::Do(void) { // create a new node - nsresult result = mDoc->CreateElement(mTag, &mNewNode); - NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); + nsresult result = mDoc->CreateElement(mTag, getter_AddRefs(mNewNode)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); - if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) + if ((NS_SUCCEEDED(result)) && (mNewNode)) { // insert the new node - nsIDOMNode *resultNode=nsnull; + nsCOMPtr resultNode; if (CreateElementTxn::eAppend==mOffsetInParent) { - result = mParent->AppendChild(mNewNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); // this object already holds a ref from CreateElement + result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode)); } else { @@ -70,12 +68,10 @@ nsresult CreateElementTxn::Do(void) result = mParent->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { - result = childNodes->Item(mOffsetInParent, &mRefNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=mRefNode)) + result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); + if ((NS_SUCCEEDED(result)) && (mRefNode)) { - result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); // this object already holds a ref from CreateElement + result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); } } } @@ -85,20 +81,16 @@ nsresult CreateElementTxn::Do(void) nsresult CreateElementTxn::Undo(void) { - nsIDOMNode *resultNode=nsnull; - nsresult result = mParent->RemoveChild(mNewNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); + nsCOMPtr resultNode; + nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); return result; } nsresult CreateElementTxn::Redo(void) { - nsIDOMNode *resultNode=nsnull; - nsresult result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); - return result; + nsCOMPtr resultNode; + nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + return result; } nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient) diff --git a/mozilla/editor/core/CreateElementTxn.h b/mozilla/editor/core/CreateElementTxn.h index 269de9c3f5a..bbe1c4a4421 100644 --- a/mozilla/editor/core/CreateElementTxn.h +++ b/mozilla/editor/core/CreateElementTxn.h @@ -20,10 +20,15 @@ #define CreateElementTxn_h__ #include "EditTxn.h" +#include "nsIDOMDocument.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsCOMPtr.h" -class nsIDOMDocument; -class nsIDOMNode; -class nsIDOMElement; +#define CREATE_ELEMENT_TXN_IID \ +{/* 7a6393c0-ac48-11d2-86d8-000064657374 */ \ +0x7a6393c0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that creates a new node in the content tree. @@ -34,11 +39,15 @@ public: enum { eAppend=-1 }; - CreateElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - const nsString& aTag, - nsIDOMNode *aParent, - PRUint32 aOffsetInParent); + virtual nsresult Init(nsIDOMDocument *aDoc, + const nsString& aTag, + nsIDOMNode *aParent, + PRUint32 aOffsetInParent); + +private: + CreateElementTxn(); + +public: virtual ~CreateElementTxn(); @@ -61,22 +70,24 @@ public: protected: /** the document into which the new node will be inserted */ - nsIDOMDocument *mDoc; + nsCOMPtr mDoc; /** the tag (mapping to object type) for the new element */ nsString mTag; /** the node into which the new node will be inserted */ - nsIDOMNode *mParent; + nsCOMPtr mParent; /** the index in mParent for the new node */ PRUint32 mOffsetInParent; /** the new node to insert */ - nsIDOMElement *mNewNode; + nsCOMPtr mNewNode; /** the node we will insert mNewNode before. We compute this ourselves. */ - nsIDOMNode *mRefNode; + nsCOMPtr mRefNode; + + friend class TransactionFactory; }; diff --git a/mozilla/editor/core/DeleteElementTxn.cpp b/mozilla/editor/core/DeleteElementTxn.cpp index 85ca3462ebb..5b55f78a54f 100644 --- a/mozilla/editor/core/DeleteElementTxn.cpp +++ b/mozilla/editor/core/DeleteElementTxn.cpp @@ -17,30 +17,32 @@ */ #include "DeleteElementTxn.h" -#include "editor.h" -#include "nsIDOMDocument.h" +#ifdef NS_DEBUG #include "nsIDOMElement.h" +#endif -// note that aEditor is not refcounted -DeleteElementTxn::DeleteElementTxn(nsEditor * aEditor, - nsIDOMDocument *aDoc, - nsIDOMNode * aElement, - nsIDOMNode * aParent) - : EditTxn(aEditor) + +DeleteElementTxn::DeleteElementTxn() + : EditTxn() { - mDoc = aDoc; - NS_ADDREF(mDoc); - mElement = aElement; - NS_ADDREF(mElement); - mParent = aParent; - NS_ADDREF(mParent); } +nsresult DeleteElementTxn::Init(nsIDOMNode *aElement, + nsIDOMNode *aParent) +{ + if ((nsnull!=aElement) && (nsnull!=aParent)) + { + mElement = aElement; + mParent = aParent; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; +} + + DeleteElementTxn::~DeleteElementTxn() { - NS_IF_RELEASE(mDoc); - NS_IF_RELEASE(mParent); - NS_IF_RELEASE(mElement); } nsresult DeleteElementTxn::Do(void) @@ -48,11 +50,32 @@ nsresult DeleteElementTxn::Do(void) if (!mParent || !mElement) return NS_ERROR_NULL_POINTER; #ifdef NS_DEBUG + // begin debug output + nsCOMPtr element=mElement; + nsAutoString elementTag="text node"; + if (element) + element->GetTagName(elementTag); + nsCOMPtr parentElement=mParent; + nsAutoString parentElementTag="text node"; + if (parentElement) + parentElement->GetTagName(parentElementTag); + char *c, *p; + c = elementTag.ToNewCString(); + p = parentElementTag.ToNewCString(); + if (c&&p) + { + printf("DeleteElementTxn: deleting child %s from parent %s\n", c, p); + delete [] c; + delete [] p; + } + // end debug output + // begin sanity check 1: parent-child relationship nsresult testResult; nsCOMPtr parentNode; testResult = mElement->GetParentNode(getter_AddRefs(parentNode)); NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent"); NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() "); + // end sanity check 1. #endif // remember which child mElement was (by remembering which child was next) diff --git a/mozilla/editor/core/DeleteElementTxn.h b/mozilla/editor/core/DeleteElementTxn.h index dce2b04dcda..9cf3469f918 100644 --- a/mozilla/editor/core/DeleteElementTxn.h +++ b/mozilla/editor/core/DeleteElementTxn.h @@ -23,8 +23,10 @@ #include "nsIDOMNode.h" #include "nsCOMPtr.h" -class nsIDOMDocument; -class nsIDOMElement; +#define DELETE_ELEMENT_TXN_IID \ +{/* 6fd77770-ac49-11d2-86d8-000064657374 */ \ +0x6fd77770, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that deletes a single element @@ -33,10 +35,13 @@ class DeleteElementTxn : public EditTxn { public: - DeleteElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - nsIDOMNode *aElement, - nsIDOMNode *aParent); + virtual nsresult Init(nsIDOMNode *aElement, + nsIDOMNode *aParent); + +private: + DeleteElementTxn(); + +public: virtual ~DeleteElementTxn(); @@ -58,14 +63,11 @@ public: protected: - /** the document into which the new node will be inserted */ - nsIDOMDocument *mDoc; - /** the element to delete */ - nsIDOMNode *mElement; + nsCOMPtr mElement; /** the node into which the new node will be inserted */ - nsIDOMNode *mParent; + nsCOMPtr mParent; /** the index in mParent for the new node */ PRUint32 mOffsetInParent; @@ -73,6 +75,8 @@ protected: /** the node we will insert mNewNode before. We compute this ourselves. */ nsCOMPtr mRefNode; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/core/DeleteRangeTxn.cpp b/mozilla/editor/core/DeleteRangeTxn.cpp index 84a1b397e4d..37a6e286434 100644 --- a/mozilla/editor/core/DeleteRangeTxn.cpp +++ b/mozilla/editor/core/DeleteRangeTxn.cpp @@ -17,18 +17,73 @@ */ #include "DeleteRangeTxn.h" -#include "editor.h" #include "nsIDOMRange.h" +#include "nsIDOMCharacterData.h" +#include "nsIDOMNodeList.h" +#include "DeleteTextTxn.h" +#include "DeleteElementTxn.h" +#include "TransactionFactory.h" +#include "nsISupportsArray.h" + +static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID); +static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); + // note that aEditor is not refcounted -DeleteRangeTxn::DeleteRangeTxn(nsEditor *aEditor, - nsIDOMRange *aRange) - : EditTxn(aEditor) +DeleteRangeTxn::DeleteRangeTxn() + : EditAggregateTxn() { - aRange->GetStartParent(getter_AddRefs(mStartParent)); - aRange->GetEndParent(getter_AddRefs(mEndParent)); - aRange->GetStartOffset(&mStartOffset); - aRange->GetEndOffset(&mEndOffset); +} + +nsresult DeleteRangeTxn::Init(nsIDOMRange *aRange) +{ + if (nsnull!=aRange) + { + nsresult result = aRange->GetStartParent(getter_AddRefs(mStartParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed."); + result = aRange->GetEndParent(getter_AddRefs(mEndParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed."); + result = aRange->GetStartOffset(&mStartOffset); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed."); + result = aRange->GetEndOffset(&mEndOffset); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed."); + result = aRange->GetCommonParent(getter_AddRefs(mCommonParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed."); + +#ifdef NS_DEBUG + PRUint32 count; + nsCOMPtr textNode; + textNode = mStartParent; + if (textNode) + textNode->GetLength(&count); + else + { + nsCOMPtr children; + result = mStartParent->GetChildNodes(getter_AddRefs(children)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad start child list"); + children->GetLength(&count); + } + NS_ASSERTION(mStartOffsetGetLength(&count); + else + { + nsCOMPtr children; + result = mEndParent->GetChildNodes(getter_AddRefs(children)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad end child list"); + children->GetLength(&count); + } + NS_ASSERTION(mEndOffset textNode; + textNode = aStartParent; // this uses implicit nsCOMPtr QI + if (textNode) + { // if the node is a text node, then delete text content + DeleteTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(textNode, aStartOffset, (aEndOffset-aStartOffset)+1); + AppendChild(txn); + } + } + else + { + PRUint32 childCount; + nsCOMPtr children; + result = aStartParent->GetChildNodes(getter_AddRefs(children)); + if ((NS_SUCCEEDED(result)) && children) + { + children->GetLength(&childCount); + NS_ASSERTION(aEndOffset child; + result = children->Item(i, getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, aStartParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + } + } + return result; +} + +nsresult DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, + PRUint32 aOffset, + nsIEditor::Direction aDir) +{ + nsresult result; + // see what kind of node we have + nsCOMPtr textNode; + textNode = aParent; // this uses implicit nsCOMPtr QI + if (textNode) + { // if the node is a text node, then delete text content + PRUint32 start, numToDelete; + if (nsIEditor::eLTR==aDir) + { + start=aOffset; + textNode->GetLength(&numToDelete); + numToDelete -= (aOffset+1); + } + else + { + start=0; + numToDelete=aOffset; + } + DeleteTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(textNode, start, numToDelete); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + else + { // we have an interior node, so delete some of its children + if (nsIEditor::eLTR==aDir) + { // delete from aOffset to end + PRUint32 childCount; + nsCOMPtr children; + result = aParent->GetChildNodes(getter_AddRefs(children)); + if ((NS_SUCCEEDED(result)) && children) + { + children->GetLength(&childCount); + PRUint32 i; + for (i=aOffset; i child; + result = children->Item(i, getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, aParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + } + } + else + { // delete from 0 to aOffset + nsCOMPtr child; + result = aParent->GetFirstChild(getter_AddRefs(child)); + for (PRUint32 i=0; iInit(child, aParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + nsCOMPtr temp = child; + result = temp->GetNextSibling(getter_AddRefs(child)); + } + } + } + + return result; +} + +nsresult DeleteRangeTxn::CreateTxnsToDeleteNodesBetween(nsIDOMNode *aCommonParent, + nsIDOMNode *aFirstChild, + nsIDOMNode *aLastChild) +{ + nsresult result; + PRBool needToProcessLastChild=PR_TRUE; // set to false if we discover we can delete all required nodes by just walking up aFirstChild's parent list + nsCOMPtr parent; // the current parent in the iteration up the ancestors + nsCOMPtr child; // the current child of parent + nsISupportsArray *ancestorList; // the ancestorList of the other endpoint, used to gate deletion + NS_NewISupportsArray(&ancestorList); + if (nsnull==ancestorList) + return NS_ERROR_NULL_POINTER; + + // Walk up the parent list of aFirstChild to aCommonParent, + // deleting all siblings to the right of the ancestors of aFirstChild. + BuildAncestorList(aLastChild, ancestorList); + child = aFirstChild; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && parent) + { + while ((NS_SUCCEEDED(result)) && child) + { // this loop starts with the first sibling of an ancestor of aFirstChild + nsCOMPtr temp = child; + result = temp->GetNextSibling(getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + if (child==aLastChild) + { // aFirstChild and aLastChild have the same parent, and we've reached aLastChild + needToProcessLastChild = PR_FALSE; + break; + } + // test if child is an ancestor of the other node. If it is, don't process this parent anymore + PRInt32 index; + index = ancestorList->IndexOf((nsISupports*)child); + if (-1!=index) + break; + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, parent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + if (parent==aCommonParent) + break; + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + + // Walk up the parent list of aLastChild to aCommonParent, + // deleting all siblings to the left of the ancestors of aLastChild. + BuildAncestorList(aFirstChild, ancestorList); + if (PR_TRUE==needToProcessLastChild) + { + child = aLastChild; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && parent) + { + while ((NS_SUCCEEDED(result)) && child) + { // this loop starts with the first sibling of an ancestor of aFirstChild + nsCOMPtr temp = child; + result = temp->GetPreviousSibling(getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + // test if child is an ancestor of the other node. If it is, don't process this parent anymore + PRInt32 index; + index = ancestorList->IndexOf((nsISupports*)child); + if (-1!=index) + break; + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, parent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + if (parent==aCommonParent) + break; + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + } + NS_RELEASE(ancestorList); + return result; +} + +nsresult DeleteRangeTxn::BuildAncestorList(nsIDOMNode *aNode, nsISupportsArray *aList) +{ + nsresult result=NS_OK; + if (nsnull!=aNode && nsnull!=aList) + { + aList->Clear(); + nsCOMPtr parent; + nsCOMPtr child = aNode; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && child && parent) + { + nsISupports * parentAsISupports; + parent->QueryInterface(nsISupports::IID(), (void **)&parentAsISupports); + aList->AppendElement(parentAsISupports); + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + } + else + result = NS_ERROR_NULL_POINTER; + + return result; +} + + diff --git a/mozilla/editor/core/DeleteRangeTxn.h b/mozilla/editor/core/DeleteRangeTxn.h index 675049f550a..09af0e639c6 100644 --- a/mozilla/editor/core/DeleteRangeTxn.h +++ b/mozilla/editor/core/DeleteRangeTxn.h @@ -19,22 +19,33 @@ #ifndef DeleteRangeTxn_h__ #define DeleteRangeTxn_h__ -#include "EditTxn.h" +#include "EditAggregateTxn.h" #include "nsIDOMNode.h" +#include "nsIEditor.h" #include "nsCOMPtr.h" class nsIDOMDocument; class nsIDOMRange; +class nsISupportsArray; + +#define DELETE_RANGE_TXN_IID \ +{/* 5ec6b260-ac49-11d2-86d8-000064657374 */ \ +0x5ec6b260, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that deletes an entire range in the content tree */ -class DeleteRangeTxn : public EditTxn +class DeleteRangeTxn : public EditAggregateTxn { public: - DeleteRangeTxn(nsEditor *aEditor, - nsIDOMRange *aRange); + virtual nsresult Init(nsIDOMRange *aRange); + +private: + DeleteRangeTxn(); + +public: virtual ~DeleteRangeTxn(); @@ -54,6 +65,23 @@ public: virtual nsresult GetRedoString(nsString **aString); +protected: + + virtual nsresult CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, + PRUint32 aStartOffset, + PRUint32 aEndOffset); + + virtual nsresult CreateTxnsToDeleteNodesBetween(nsIDOMNode *aParent, + nsIDOMNode *aFirstChild, + nsIDOMNode *aLastChild); + + virtual nsresult CreateTxnsToDeleteContent(nsIDOMNode *aParent, + PRUint32 aOffset, + nsIEditor::Direction aDir); + + virtual nsresult BuildAncestorList(nsIDOMNode *aNode, + nsISupportsArray *aList); + protected: /** p1 in the range */ @@ -65,9 +93,14 @@ protected: /** p2 in the range */ nsCOMPtr mEndParent; + /** the closest common parent of p1 and p2 */ + nsCOMPtr mCommonParent; + /** p2 offset */ PRInt32 mEndOffset; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/core/DeleteTextTxn.cpp b/mozilla/editor/core/DeleteTextTxn.cpp index 09df4b2b0ab..eda056e3a3a 100644 --- a/mozilla/editor/core/DeleteTextTxn.cpp +++ b/mozilla/editor/core/DeleteTextTxn.cpp @@ -17,20 +17,23 @@ */ #include "DeleteTextTxn.h" -#include "editor.h" #include "nsIDOMCharacterData.h" -// note that aEditor is not refcounted -DeleteTextTxn::DeleteTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, + +DeleteTextTxn::DeleteTextTxn() + : EditTxn() +{ +} + +nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aNumCharsToDelete) - : EditTxn(aEditor) { mElement = aElement; mOffset = aOffset; mNumCharsToDelete = aNumCharsToDelete; mDeletedText = ""; + return NS_OK; } nsresult DeleteTextTxn::Do(void) diff --git a/mozilla/editor/core/DeleteTextTxn.h b/mozilla/editor/core/DeleteTextTxn.h index 73d2e35ae08..ad2fd0de334 100644 --- a/mozilla/editor/core/DeleteTextTxn.h +++ b/mozilla/editor/core/DeleteTextTxn.h @@ -20,8 +20,13 @@ #define DeleteTextTxn_h__ #include "EditTxn.h" +#include "nsIDOMCharacterData.h" +#include "nsCOMPtr.h" -class nsIDOMCharacterData; +#define DELETE_TEXT_TXN_IID \ +{/* 4d3a2720-ac49-11d2-86d8-000064657374 */ \ +0x4d3a2720, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that changes an attribute of a content node. @@ -31,11 +36,15 @@ class DeleteTextTxn : public EditTxn { public: - DeleteTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, - PRUint32 aOffset, - PRUint32 aNumCharsToDelete); + virtual nsresult Init(nsIDOMCharacterData *aElement, + PRUint32 aOffset, + PRUint32 aNumCharsToDelete); +private: + DeleteTextTxn(); + +public: + virtual nsresult Do(void); virtual nsresult Undo(void); @@ -53,7 +62,7 @@ public: protected: /** the text element to operate upon */ - nsIDOMCharacterData *mElement; + nsCOMPtr mElement; /** the offset into mElement where the deletion is to take place */ PRUint32 mOffset; @@ -64,6 +73,8 @@ protected: /** the text that was deleted */ nsString mDeletedText; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/core/EditAggregateTxn.cpp b/mozilla/editor/core/EditAggregateTxn.cpp new file mode 100644 index 00000000000..e07c6d3f8c6 --- /dev/null +++ b/mozilla/editor/core/EditAggregateTxn.cpp @@ -0,0 +1,145 @@ +/* -*- 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 "EditAggregateTxn.h" +#include "nsCOMPtr.h" +#include "nsIDOMNode.h" +#include "nsVoidArray.h" + +EditAggregateTxn::EditAggregateTxn() + : EditTxn() +{ + mChildren = new nsVoidArray(); +} + +EditAggregateTxn::~EditAggregateTxn() +{ + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + NS_IF_RELEASE(txn); + } + delete mChildren; + } +} + +nsresult EditAggregateTxn::Do(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + result = txn->Do(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::Undo(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + // undo goes through children backwards + for (i=count-1; i>=0; i--) + { + EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i)); + result = txn->Undo(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::Redo(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + result = txn->Redo(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::GetIsTransient(PRBool *aIsTransient) +{ + if (nsnull!=aIsTransient) + *aIsTransient = PR_TRUE; + return NS_OK; +} + +nsresult EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) +{ + return NS_OK; +} + +nsresult EditAggregateTxn::Write(nsIOutputStream *aOutputStream) +{ + return NS_OK; +} + +nsresult EditAggregateTxn::GetUndoString(nsString **aString) +{ + if (nsnull!=aString) + *aString=nsnull; + return NS_OK; +} + +nsresult EditAggregateTxn::GetRedoString(nsString **aString) +{ + if (nsnull!=aString) + *aString=nsnull; + return NS_OK; +} + +nsresult EditAggregateTxn::AppendChild(EditTxn *aTxn) +{ + if ((nsnull!=mChildren) && (nsnull!=aTxn)) + { + mChildren->AppendElement(aTxn); + return NS_OK; + } + return NS_ERROR_NULL_POINTER; +} + + + + + + diff --git a/mozilla/editor/core/EditAggregateTxn.h b/mozilla/editor/core/EditAggregateTxn.h new file mode 100644 index 00000000000..9830a1d0656 --- /dev/null +++ b/mozilla/editor/core/EditAggregateTxn.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 EditAggregateTxn_h__ +#define EditAggregateTxn_h__ + +#include "EditTxn.h" + +#define EDIT_AGGREGATE_TXN_IID \ +{/* 345921a0-ac49-11d2-86d8-000064657374 */ \ +0x345921a0, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + +class nsVoidArray; + +/** + * base class for all document editing transactions that require aggregation. + * provides a list of child transactions. + */ +class EditAggregateTxn : public EditTxn +{ +public: + + EditAggregateTxn(); + + virtual ~EditAggregateTxn(); + + virtual nsresult Do(void); + + virtual nsresult Undo(void); + + virtual nsresult Redo(void); + + virtual nsresult GetIsTransient(PRBool *aIsTransient); + + virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction); + + virtual nsresult Write(nsIOutputStream *aOutputStream); + + virtual nsresult GetUndoString(nsString **aString); + + virtual nsresult GetRedoString(nsString **aString); + + virtual nsresult AppendChild(EditTxn *aTxn); + +protected: + + nsVoidArray *mChildren; + +}; + +#endif \ No newline at end of file diff --git a/mozilla/editor/core/EditTxn.cpp b/mozilla/editor/core/EditTxn.cpp index 768bef6604e..d17b6d2583f 100644 --- a/mozilla/editor/core/EditTxn.cpp +++ b/mozilla/editor/core/EditTxn.cpp @@ -28,10 +28,8 @@ NS_IMPL_ADDREF(EditTxn) NS_IMPL_RELEASE(EditTxn) // note that aEditor is not refcounted -EditTxn::EditTxn(nsEditor *aEditor) +EditTxn::EditTxn() { - NS_ASSERTION(nsnull!=aEditor, "null aEditor arg to EditTxn constructor"); - mEditor = aEditor; } nsresult EditTxn::Do(void) diff --git a/mozilla/editor/core/EditTxn.h b/mozilla/editor/core/EditTxn.h index afd99fbed46..97381e64dc5 100644 --- a/mozilla/editor/core/EditTxn.h +++ b/mozilla/editor/core/EditTxn.h @@ -20,13 +20,17 @@ #define EditTxn_h__ #include "nsITransaction.h" -class nsEditor; class nsIDOMNode; +#define EDIT_TXN_IID \ +{/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \ +0xc5ea31b0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + /** * base class for all document editing transactions. - * provides access to the nsEditor that created this transaction. + * provides default concrete behavior for all nsITransaction methods. */ class EditTxn : public nsITransaction { @@ -34,7 +38,7 @@ public: NS_DECL_ISUPPORTS - EditTxn(nsEditor *aEditor); + EditTxn(); virtual nsresult Do(void); @@ -52,10 +56,6 @@ public: virtual nsresult GetRedoString(nsString **aString); -protected: - - nsEditor *mEditor; - }; #endif \ No newline at end of file diff --git a/mozilla/editor/core/InsertTextTxn.cpp b/mozilla/editor/core/InsertTextTxn.cpp index 4d078cc193f..220c9e46916 100644 --- a/mozilla/editor/core/InsertTextTxn.cpp +++ b/mozilla/editor/core/InsertTextTxn.cpp @@ -20,18 +20,22 @@ #include "editor.h" #include "nsIDOMCharacterData.h" -static NS_DEFINE_IID(kInsertTextTxnIID, INSERTTEXTTXN_IID); +static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID); -// note that aEditor is not refcounted -InsertTextTxn::InsertTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, + +InsertTextTxn::InsertTextTxn() + : EditTxn() +{ +} + +nsresult InsertTextTxn::Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, const nsString& aStringToInsert) - : EditTxn(aEditor) { mElement = aElement; mOffset = aOffset; mStringToInsert = aStringToInsert; + return NS_OK; } nsresult InsertTextTxn::Do(void) @@ -60,11 +64,11 @@ nsresult InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) if ((nsnull!=aDidMerge) && (nsnull!=aTransaction)) { // if aTransaction isa InsertTextTxn, absorb it - nsCOMPtr otherTxn; - nsresult result = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); + nsCOMPtr otherTxn = aTransaction; + nsresult result=NS_OK;// = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); if (NS_SUCCEEDED(result) && (otherTxn)) { - nsString otherData; + nsAutoString otherData; otherTxn->GetData(otherData); mStringToInsert += otherData; } diff --git a/mozilla/editor/core/InsertTextTxn.h b/mozilla/editor/core/InsertTextTxn.h index 1e7288bb88d..134cddb790b 100644 --- a/mozilla/editor/core/InsertTextTxn.h +++ b/mozilla/editor/core/InsertTextTxn.h @@ -20,14 +20,14 @@ #define InsertTextTxn_h__ #include "EditTxn.h" +#include "nsIDOMCharacterData.h" +#include "nsCOMPtr.h" -#define INSERTTEXTTXN_IID \ +#define INSERT_TEXT_TXN_IID \ {/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \ 0x93276f00, 0xab2c, 0x11d2, \ {0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} } -class nsIDOMCharacterData; - /** * A transaction that changes an attribute of a content node. * This transaction covers add, remove, and change attribute. @@ -36,15 +36,13 @@ class InsertTextTxn : public EditTxn { public: - InsertTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert); + virtual nsresult Init(nsIDOMCharacterData *aElement, + PRUint32 aOffset, + const nsString& aStringToInsert); private: - // default ctor so that nsCOMPtr is happy - InsertTextTxn() : EditTxn(nsnull) {} + InsertTextTxn(); public: @@ -67,7 +65,7 @@ public: // override QueryInterface to handle InsertTextTxn request NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); - static const nsIID& IID() { static nsIID iid = INSERTTEXTTXN_IID; return iid; } + static const nsIID& IID() { static nsIID iid = INSERT_TEXT_TXN_IID; return iid; } virtual nsresult GetData(nsString& aResult); @@ -75,7 +73,7 @@ public: protected: /** the text element to operate upon */ - nsIDOMCharacterData *mElement; + nsCOMPtr mElement; /** the offset into mElement where the insertion is to take place */ PRUint32 mOffset; @@ -86,6 +84,8 @@ protected: /** the text to insert into mElement at mOffset */ nsString mStringToInsert; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/core/JoinElementTxn.cpp b/mozilla/editor/core/JoinElementTxn.cpp new file mode 100644 index 00000000000..e6331ffddf9 --- /dev/null +++ b/mozilla/editor/core/JoinElementTxn.cpp @@ -0,0 +1,122 @@ +/* -*- 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 "JoinElementTxn.h" +#include "nsIDOMNodeList.h" +#include "editor.h" + + +JoinElementTxn::JoinElementTxn() + : EditTxn() +{ +} + +nsresult JoinElementTxn::Init(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode) +{ + mLeftNode = aLeftNode; + mRightNode = aRightNode; + mOffset=0; + return NS_OK; +} + +JoinElementTxn::~JoinElementTxn() +{ +} + +nsresult JoinElementTxn::Do(void) +{ + nsresult result; + + if ((mLeftNode) && (mRightNode)) + { // get the parent node + nsCOMPtrleftParent; + result = mLeftNode->GetParentNode(getter_AddRefs(leftParent)); + if ((NS_SUCCEEDED(result)) && (leftParent)) + { // verify that mLeftNode and mRightNode have the same parent + nsCOMPtrrightParent; + result = mRightNode->GetParentNode(getter_AddRefs(rightParent)); + if ((NS_SUCCEEDED(result)) && (rightParent)) + { + if (leftParent==rightParent) + { + mParent=leftParent; // set this instance mParent. + // Other methods see a non-null mParent and know all is well + nsCOMPtr childNodes; + result = mLeftNode->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(result)) && (childNodes)) + { + childNodes->GetLength(&mOffset); + } + result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE); + } + } + } + } + return result; +} + + +nsresult JoinElementTxn::Undo(void) +{ + nsresult result = nsEditor::SplitNode(mRightNode, mOffset, mLeftNode, mParent); + return result; +} + +nsresult JoinElementTxn::Redo(void) +{ + nsresult result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE); + return result; +} + +nsresult JoinElementTxn::GetIsTransient(PRBool *aIsTransient) +{ + if (nsnull!=aIsTransient) + *aIsTransient = PR_FALSE; + return NS_OK; +} + +nsresult JoinElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) +{ + if (nsnull!=aDidMerge) + *aDidMerge=PR_FALSE; + return NS_OK; +} + +nsresult JoinElementTxn::Write(nsIOutputStream *aOutputStream) +{ + return NS_OK; +} + +nsresult JoinElementTxn::GetUndoString(nsString **aString) +{ + if (nsnull!=aString) + { + **aString="Join Element"; + } + return NS_OK; +} + +nsresult JoinElementTxn::GetRedoString(nsString **aString) +{ + if (nsnull!=aString) + { + **aString="Split Element"; + } + return NS_OK; +} diff --git a/mozilla/editor/core/JoinElementTxn.h b/mozilla/editor/core/JoinElementTxn.h new file mode 100644 index 00000000000..d094efe5f09 --- /dev/null +++ b/mozilla/editor/core/JoinElementTxn.h @@ -0,0 +1,86 @@ +/* -*- 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 JoinElementTxn_h__ +#define JoinElementTxn_h__ + +#include "EditTxn.h" +#include "nsIDOMNode.h" +#include "nsCOMPtr.h" + +#define JOIN_ELEMENT_TXN_IID \ +{/* 9bc5f9f0-ac48-11d2-86d8-000064657374 */ \ +0x9bc5f9f0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + + +/** + * A transaction that joins two elements E1 and E2 into a single node E. + * The children of E are the children of E1 followed by the children of E2. + */ +class JoinElementTxn : public EditTxn +{ +public: + + virtual nsresult Init(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode); +protected: + JoinElementTxn(); + +public: + + virtual ~JoinElementTxn(); + + virtual nsresult Do(void); + + virtual nsresult Undo(void); + + virtual nsresult Redo(void); + + virtual nsresult GetIsTransient(PRBool *aIsTransient); + + virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction); + + virtual nsresult Write(nsIOutputStream *aOutputStream); + + virtual nsresult GetUndoString(nsString **aString); + + virtual nsresult GetRedoString(nsString **aString); + +protected: + + /** the elements to operate upon. + * After the merge, mRightNode remains and mLeftNode is removed from the content tree. + */ + nsCOMPtr mLeftNode; + nsCOMPtr mRightNode; + + /** the offset into mNode where the children of mElement are split (for undo).
+ * mOffset is the index of the last child in the left node. + * -1 means the left node gets no children. + */ + PRUint32 mOffset; + + /** the parent node containing mLeftNode and mRightNode */ + nsCOMPtr mParent; + + friend class TransactionFactory; + +}; + +#endif diff --git a/mozilla/editor/core/Makefile.in b/mozilla/editor/core/Makefile.in index e962b20f862..a962248fc9e 100644 --- a/mozilla/editor/core/Makefile.in +++ b/mozilla/editor/core/Makefile.in @@ -30,12 +30,15 @@ CPPSRCS = \ nsEditFactory.cpp \ ChangeAttributeTxn.cpp \ EditTxn.cpp \ + EditAggregateTxn.cpp \ InsertTextTxn.cpp \ DeleteTextTxn.cpp \ CreateElementTxn.cpp \ DeleteElementTxn.cpp \ DeleteRangeTxn.cpp \ SplitElementTxn.cpp \ + JoinElementTxn.cpp \ + TransactionFactory.cpp \ $(NULL) MODULE = editor diff --git a/mozilla/editor/core/SplitElementTxn.cpp b/mozilla/editor/core/SplitElementTxn.cpp index 2134cc5375c..dcabdec1865 100644 --- a/mozilla/editor/core/SplitElementTxn.cpp +++ b/mozilla/editor/core/SplitElementTxn.cpp @@ -17,44 +17,42 @@ */ #include "SplitElementTxn.h" -#include "editor.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" +#include "editor.h" // note that aEditor is not refcounted -SplitElementTxn::SplitElementTxn(nsEditor *aEditor, - nsIDOMNode *aNode, - PRInt32 aOffset) - : EditTxn(aEditor) +SplitElementTxn::SplitElementTxn() + : EditTxn() { - mNode = aNode; - NS_ADDREF(mNode); +} + +nsresult SplitElementTxn::Init(nsIDOMNode *aNode, + PRInt32 aOffset) +{ + mExistingRightNode = aNode; mOffset = aOffset; - mNewNode = nsnull; - mParent = nsnull; + return NS_OK; } SplitElementTxn::~SplitElementTxn() { - NS_IF_RELEASE(mNode); - NS_IF_RELEASE(mNewNode); - NS_IF_RELEASE(mParent); } nsresult SplitElementTxn::Do(void) { // create a new node - nsresult result = mNode->CloneNode(PR_FALSE, &mNewNode); - NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); + nsresult result = mExistingRightNode->CloneNode(PR_FALSE, getter_AddRefs(mNewLeftNode)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element."); - if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) + if ((NS_SUCCEEDED(result)) && (mNewLeftNode)) { // get the parent node - result = mNode->GetParentNode(&mParent); + result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent)); // insert the new node - if ((NS_SUCCEEDED(result)) && (nsnull!=mParent)) + if ((NS_SUCCEEDED(result)) && (mParent)) { - result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); + result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent); } } return result; @@ -63,13 +61,13 @@ nsresult SplitElementTxn::Do(void) nsresult SplitElementTxn::Undo(void) { // this assumes Do inserted the new node in front of the prior existing node - nsresult result = mEditor->JoinNodes(mNode, mNewNode, mParent, PR_FALSE); + nsresult result = nsEditor::JoinNodes(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); return result; } nsresult SplitElementTxn::Redo(void) { - nsresult result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); + nsresult result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent); return result; } diff --git a/mozilla/editor/core/SplitElementTxn.h b/mozilla/editor/core/SplitElementTxn.h index 8fb19df1fb6..051021e6bb8 100644 --- a/mozilla/editor/core/SplitElementTxn.h +++ b/mozilla/editor/core/SplitElementTxn.h @@ -20,10 +20,13 @@ #define SplitElementTxn_h__ #include "EditTxn.h" +#include "nsIDOMNode.h" +#include "nsCOMPtr.h" -class nsIDOMNode; -class nsIDOMElement; -class nsIDOMDocument; +#define SPLIT_ELEMENT_TXN_IID \ +{/* 690c6290-ac48-11d2-86d8-000064657374 */ \ +0x690c6290, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that splits an element E into two identical nodes, E1 and E2 @@ -33,10 +36,12 @@ class SplitElementTxn : public EditTxn { public: - SplitElementTxn(nsEditor *aEditor, - nsIDOMNode *aNode, - PRInt32 aOffset); + virtual nsresult Init (nsIDOMNode *aNode, + PRInt32 aOffset); +protected: + SplitElementTxn(); +public: virtual ~SplitElementTxn(); virtual nsresult Do(void); @@ -58,7 +63,7 @@ public: protected: /** the element to operate upon */ - nsIDOMNode *mNode; + nsCOMPtr mExistingRightNode; /** the offset into mElement where the children of mElement are split.
* mOffset is the index of the last child in the left node. @@ -67,8 +72,12 @@ protected: PRInt32 mOffset; /** the element we create when splitting mElement */ - nsIDOMNode *mNewNode; - nsIDOMNode *mParent; + nsCOMPtr mNewLeftNode; + + /** the parent shared by mExistingRightNode and mNewLeftNode */ + nsCOMPtr mParent; + + friend class TransactionFactory; }; diff --git a/mozilla/editor/core/TransactionFactory.cpp b/mozilla/editor/core/TransactionFactory.cpp new file mode 100644 index 00000000000..cc4be1af14f --- /dev/null +++ b/mozilla/editor/core/TransactionFactory.cpp @@ -0,0 +1,75 @@ +/* -*- 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 "TransactionFactory.h" +// transactions this factory knows how to build +#include "InsertTextTxn.h" +#include "DeleteTextTxn.h" +#include "CreateElementTxn.h" +#include "DeleteElementTxn.h" +#include "DeleteRangeTxn.h" +#include "ChangeAttributeTxn.h" +#include "SplitElementTxn.h" +#include "JoinElementTxn.h" + +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); +static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID); +static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID); +static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID); + +TransactionFactory::TransactionFactory() +{ +} + +TransactionFactory::~TransactionFactory() +{ +} + +nsresult +TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult) +{ + nsresult result = NS_OK; + *aResult = nsnull; + if (aTxnType.Equals(kInsertTextTxnIID)) + *aResult = new InsertTextTxn(); + else if (aTxnType.Equals(kDeleteTextTxnIID)) + *aResult = new DeleteTextTxn(); + else if (aTxnType.Equals(kCreateElementTxnIID)) + *aResult = new CreateElementTxn(); + else if (aTxnType.Equals(kDeleteElementTxnIID)) + *aResult = new DeleteElementTxn(); + else if (aTxnType.Equals(kDeleteRangeTxnIID)) + *aResult = new DeleteRangeTxn(); + else if (aTxnType.Equals(kChangeAttributeTxnIID)) + *aResult = new ChangeAttributeTxn(); + else if (aTxnType.Equals(kSplitElementTxnIID)) + *aResult = new SplitElementTxn(); + else if (aTxnType.Equals(kJoinElementTxnIID)) + *aResult = new JoinElementTxn(); + + if (nsnull==*aResult) + result = NS_ERROR_INVALID_ARG; + + return result; +} + + diff --git a/mozilla/editor/core/TransactionFactory.h b/mozilla/editor/core/TransactionFactory.h new file mode 100644 index 00000000000..58276d5679f --- /dev/null +++ b/mozilla/editor/core/TransactionFactory.h @@ -0,0 +1,41 @@ +/* -*- 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 TransactionFactory_h__ +#define TransactionFactory_h__ + +#include "nsISupports.h" + +class EditTxn; + +/** + * This class instantiates and optionally recycles edit transactions + * A recycler would be a separate static object, since this class does not get instantiated + */ +class TransactionFactory +{ +protected: + TransactionFactory(); + virtual ~TransactionFactory(); + +public: + static nsresult GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult); + +}; + +#endif diff --git a/mozilla/editor/core/editor.cpp b/mozilla/editor/core/editor.cpp index 0eeeee4a365..ad002f187fa 100644 --- a/mozilla/editor/core/editor.cpp +++ b/mozilla/editor/core/editor.cpp @@ -33,16 +33,22 @@ #include "nsTransactionManagerCID.h" #include "nsITransactionManager.h" #include "nsIPresShell.h" +#include "nsIViewManager.h" #include "nsISelection.h" +#include "nsICollection.h" +#include "nsIEnumerator.h" #include "nsIAtom.h" // transactions the editor knows how to build +#include "TransactionFactory.h" #include "ChangeAttributeTxn.h" #include "CreateElementTxn.h" #include "DeleteElementTxn.h" #include "InsertTextTxn.h" #include "DeleteTextTxn.h" #include "DeleteRangeTxn.h" +#include "SplitElementTxn.h" +#include "JoinElementTxn.h" static NS_DEFINE_IID(kIDOMEventReceiverIID, NS_IDOMEVENTRECEIVER_IID); @@ -58,8 +64,18 @@ static NS_DEFINE_IID(kIEditFactoryIID, NS_IEDITORFACTORY_IID); static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID); static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kEditorCID, NS_EDITOR_CID); +// transaction manager static NS_DEFINE_IID(kITransactionManagerIID, NS_ITRANSACTIONMANAGER_IID); static NS_DEFINE_CID(kCTransactionManagerFactoryCID, NS_TRANSACTION_MANAGER_FACTORY_CID); +// transactions +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); +static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID); +static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID); +static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID); #ifdef XP_PC @@ -165,6 +181,7 @@ nsEditor::nsEditor() nsEditor::~nsEditor() { NS_IF_RELEASE(mPresShell); + NS_IF_RELEASE(mViewManager); NS_IF_RELEASE(mTxnMgr); //the autopointers will clear themselves up. @@ -229,6 +246,8 @@ nsEditor::Init(nsIDOMDocument *aDomInterface, nsIPresShell* aPresShell) mDomInterfaceP = aDomInterface; mPresShell = aPresShell; NS_ADDREF(mPresShell); + mViewManager = mPresShell->GetViewManager(); + mUpdateCount=0; nsresult t_result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this); if (NS_OK != t_result) @@ -308,13 +327,13 @@ nsEditor::Init(nsIDOMDocument *aDomInterface, nsIPresShell* aPresShell) nsresult nsEditor::InsertString(nsString *aString) { - return AppendText(aString); + return NS_OK; } nsresult -nsEditor::SetProperties(PROPERTIES aProperty) +nsEditor::SetProperties(Properties aProperties) { return NS_OK; } @@ -322,7 +341,7 @@ nsEditor::SetProperties(PROPERTIES aProperty) nsresult -nsEditor::GetProperties(PROPERTIES **) +nsEditor::GetProperties(Properties **aProperties) { return NS_OK; } @@ -334,9 +353,13 @@ nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, cons nsresult result; if (nsnull != aElement) { - ChangeAttributeTxn *txn = new ChangeAttributeTxn(this, aElement, aAttribute, aValue, PR_FALSE); + ChangeAttributeTxn *txn; + result = TransactionFactory::GetNewTransaction(kChangeAttributeTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + txn->Init(this, aElement, aAttribute, aValue, PR_FALSE); + result = Do(txn); + } } return result; } @@ -369,10 +392,14 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute) nsresult result; if (nsnull != aElement) { - nsString value; - ChangeAttributeTxn *txn = new ChangeAttributeTxn(this, aElement, aAttribute, value, PR_TRUE); + ChangeAttributeTxn *txn; + result = TransactionFactory::GetNewTransaction(kChangeAttributeTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + nsAutoString value; + txn->Init(this, aElement, aAttribute, value, PR_TRUE); + result = Do(txn); + } } return result; } @@ -420,26 +447,6 @@ nsEditor::MouseClick(int aX,int aY) //BEGIN nsEditor Private methods - -nsresult -nsEditor::AppendText(nsString *aStr) -{ - nsCOMPtr currentNode; - nsCOMPtr textNode; - nsCOMPtr text; - if (!aStr) - return NS_ERROR_NULL_POINTER; - if (NS_SUCCEEDED(GetCurrentNode(getter_AddRefs(currentNode))) && - NS_SUCCEEDED(GetFirstTextNode(currentNode,getter_AddRefs(textNode))) && - NS_SUCCEEDED(textNode->QueryInterface(kIDOMTextIID, getter_AddRefs(text)))) { - text->AppendData(*aStr); - } - - return NS_OK; -} - - - nsresult nsEditor::GetCurrentNode(nsIDOMNode ** aNode) { @@ -485,7 +492,7 @@ nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDO while (childNode) { result = childNode->QueryInterface(kIDOMNodeIID,getter_AddRefs(element)); - nsString tag; + nsAutoString tag; if (NS_SUCCEEDED(result) && (element)) { element->GetTagName(tag); @@ -500,9 +507,8 @@ nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDO return result; } } - nsCOMPtr xNode; - childNode->GetNextSibling(getter_AddRefs(xNode)); - childNode=xNode; + nsCOMPtr temp = childNode; + temp->GetNextSibling(getter_AddRefs(childNode)); } return NS_ERROR_FAILURE; @@ -559,7 +565,7 @@ nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode) } nsresult -nsEditor::ExecuteTransaction(nsITransaction *aTxn) +nsEditor::Do(nsITransaction *aTxn) { nsresult result = NS_OK; if (nsnull!=aTxn) @@ -577,27 +583,65 @@ nsEditor::ExecuteTransaction(nsITransaction *aTxn) } nsresult -nsEditor::Undo() +nsEditor::Undo(PRUint32 aCount) { nsresult result = NS_OK; if (nsnull!=mTxnMgr) { - result = mTxnMgr->Undo(); + PRUint32 i=0; + for ( ; iUndo(); + if (NS_FAILED(result)) + break; + } } return result; } nsresult -nsEditor::Redo() +nsEditor::Redo(PRUint32 aCount) { nsresult result = NS_OK; if (nsnull!=mTxnMgr) { - result = mTxnMgr->Redo(); + PRUint32 i=0; + for ( ; iRedo(); + if (NS_FAILED(result)) + break; + } } return result; } +nsresult +nsEditor::BeginUpdate() +{ + NS_PRECONDITION(mUpdateCount>=0, "bad state"); + if (nsnull!=mViewManager) + { + if (0==mUpdateCount) + mViewManager->DisableRefresh(); + mUpdateCount++; + } + return NS_OK; +} + +nsresult +nsEditor::EndUpdate() +{ + NS_PRECONDITION(mUpdateCount>0, "bad state"); + if (nsnull!=mViewManager) + { + mUpdateCount--; + if (0==mUpdateCount) + mViewManager->EnableRefresh(); + } + return NS_OK; +} + nsresult nsEditor::Delete(PRBool aForward, PRUint32 aCount) { return NS_OK; @@ -610,9 +654,13 @@ nsresult nsEditor::CreateElement(const nsString& aTag, nsresult result; if (nsnull != aParent) { - CreateElementTxn *txn = new CreateElementTxn(this, mDomInterfaceP, aTag, aParent, aPosition); + CreateElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kCreateElementTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + txn->Init(mDomInterfaceP, aTag, aParent, aPosition); + result = Do(txn); + } else result = NS_ERROR_OUT_OF_MEMORY; } @@ -628,9 +676,13 @@ nsresult nsEditor::DeleteElement(nsIDOMNode * aParent, nsresult result; if ((nsnull != aParent) && (nsnull != aElement)) { - DeleteElementTxn *txn = new DeleteElementTxn(this, mDomInterfaceP, aElement, aParent); + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + txn->Init(aElement, aParent); + result = Do(txn); + } else result = NS_ERROR_OUT_OF_MEMORY; } @@ -640,18 +692,51 @@ nsresult nsEditor::DeleteElement(nsIDOMNode * aParent, return result; } -nsresult nsEditor::InsertText(nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert) +// XXX; factor -- should call BuildInsertTextTransaction to do most of the work +nsresult nsEditor::InsertText(const nsString& aStringToInsert) { nsresult result; - if (nsnull != aElement) + nsISelection* selection; + result = mPresShell->GetSelection(&selection); + if ((NS_SUCCEEDED(result)) && (nsnull!=selection)) { - InsertTextTxn *txn = new InsertTextTxn(this, aElement, aOffset, aStringToInsert); - if (nsnull!=txn) - result = ExecuteTransaction(txn); - else - result = NS_ERROR_OUT_OF_MEMORY; + nsCOMPtr enumerator; + enumerator = selection; + if (enumerator) + { + enumerator->First(); + nsISupports *currentItem; + result = enumerator->CurrentItem(¤tItem); + if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) + { + // XXX: we'll want to deleteRange if the selection isn't just an insertion point + // for now, just insert text after the start of the first node + nsCOMPtr range = currentItem; + if (range) + { + nsCOMPtr node; + result = range->GetStartParent(getter_AddRefs(node)); + if ((NS_SUCCEEDED(result)) && (node)) + { + nsCOMPtr nodeAsText = node; + if (nodeAsText) + { + PRInt32 offset; + range->GetStartOffset(&offset); + InsertTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kInsertTextTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(nodeAsText, offset, aStringToInsert); + result = Do(txn); + } + else + result = NS_ERROR_OUT_OF_MEMORY; + } + } + } + } + } } else result = NS_ERROR_INVALID_ARG; @@ -659,6 +744,7 @@ nsresult nsEditor::InsertText(nsIDOMCharacterData *aElement, return result; } +// XXX; factor -- should call BuildDeleteTextTransaction to do most of the work nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aLength) @@ -666,9 +752,13 @@ nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement, nsresult result=NS_OK; if (nsnull != aElement) { - DeleteTextTxn *txn = new DeleteTextTxn(this, aElement, aOffset, aLength); + DeleteTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); if (nsnull!=txn) - result = ExecuteTransaction(txn); + { + txn->Init(aElement, aOffset, aLength); + result = Do(txn); + } else result = NS_ERROR_OUT_OF_MEMORY; } @@ -678,6 +768,10 @@ nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement, return result; } +// XXX; factor -- should call DeleteSelectionTransaction to do most of the work +// XXX: these should get wrapped up in a single composite transaction +// rather than executing each individually, maybe I should alloc a generic aggregate +// and stick each in there, then execute the aggregate nsresult nsEditor::DeleteSelection() { nsresult result; @@ -685,15 +779,34 @@ nsresult nsEditor::DeleteSelection() result = mPresShell->GetSelection(&selection); if ((NS_SUCCEEDED(result)) && (nsnull!=selection)) { - nsCOMPtr range; - result = selection->QueryInterface(kIDOMRangeIID, getter_AddRefs(range)); - if ((NS_SUCCEEDED(result)) && (range)) + nsCOMPtr enumerator; + enumerator = selection; + if (enumerator) { - DeleteRangeTxn *txn = new DeleteRangeTxn(this, range); - if (nsnull!=txn) - result = ExecuteTransaction(txn); - else - result = NS_ERROR_OUT_OF_MEMORY; + enumerator->First(); + nsISupports *currentItem; + result = enumerator->CurrentItem(¤tItem); + if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) + { + nsCOMPtr range = currentItem; + DeleteRangeTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteRangeTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(range); + result = Do(txn); + } + else + result = NS_ERROR_OUT_OF_MEMORY; + if (NS_SUCCEEDED(result)) + { + nsresult nextResult = enumerator->Next(); + if (NS_SUCCEEDED(nextResult)) + { + result = enumerator->CurrentItem(¤tItem); + } + } + } } } else @@ -703,30 +816,31 @@ nsresult nsEditor::DeleteSelection() } nsresult -nsEditor::SplitNode(nsIDOMNode * aNode, +nsEditor::SplitNode(nsIDOMNode * aExistingRightNode, PRInt32 aOffset, - nsIDOMNode* aNewNode, + nsIDOMNode* aNewLeftNode, nsIDOMNode* aParent) { nsresult result; - NS_ASSERTION(((nsnull!=aNode) && - (nsnull!=aNewNode) && + NS_ASSERTION(((nsnull!=aExistingRightNode) && + (nsnull!=aNewLeftNode) && (nsnull!=aParent)), "null arg"); - if ((nsnull!=aNode) && - (nsnull!=aNewNode) && + if ((nsnull!=aExistingRightNode) && + (nsnull!=aNewLeftNode) && (nsnull!=aParent)) { nsCOMPtr resultNode; - result = aParent->InsertBefore(aNewNode, aNode, getter_AddRefs(resultNode)); + result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode)); if (NS_SUCCEEDED(result)) { // split the children between the 2 nodes - // at this point, nNode has all the children + // at this point, aExistingRightNode has all the children + // move all the children whose index is < aOffset to aNewLeftNode if (0<=aOffset) // don't bother unless we're going to move at least one child { nsCOMPtr childNodes; - result = aParent->GetChildNodes(getter_AddRefs(childNodes)); + result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { PRInt32 i=0; @@ -736,10 +850,10 @@ nsEditor::SplitNode(nsIDOMNode * aNode, result = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(result)) && (childNode)) { - result = aNode->RemoveChild(childNode, getter_AddRefs(resultNode)); + result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode)); if (NS_SUCCEEDED(result)) { - result = aNewNode->AppendChild(childNode, getter_AddRefs(resultNode)); + result = aNewLeftNode->AppendChild(childNode, getter_AddRefs(resultNode)); } } } diff --git a/mozilla/editor/core/editor.h b/mozilla/editor/core/editor.h index 243f9339873..7a2b7790b7f 100644 --- a/mozilla/editor/core/editor.h +++ b/mozilla/editor/core/editor.h @@ -16,6 +16,9 @@ * Reserved. */ +#ifndef __editor_h__ +#define __editor_h__ + #include "prmon.h" #include "nsIEditor.h" #include "nsIContextLoader.h" @@ -24,11 +27,13 @@ #include "nsCOMPtr.h" #include "editorInterfaces.h" #include "nsITransactionManager.h" +#include "TransactionFactory.h" #include "nsRepository.h" //#include "nsISelection.h" class nsIDOMCharacterData; class nsIPresShell; +class nsIViewManager; //This is the monitor for the editor. PRMonitor *getEditorMonitor(); @@ -42,15 +47,19 @@ PRMonitor *getEditorMonitor(); class nsEditor : public nsIEditor { private: - nsIPresShell *mPresShell; + nsIPresShell *mPresShell; + nsIViewManager *mViewManager; + PRUint32 mUpdateCount; nsCOMPtr mDomInterfaceP; nsCOMPtr mKeyListenerP; nsCOMPtr mMouseListenerP; // nsCOMPtr mSelectionP; //nsCOMPtr mTxnMgrP; + nsITransactionManager * mTxnMgr; friend PRBool NSCanUnload(void); static PRInt32 gInstanceCount; + public: /** The default constructor. This should suffice. the setting of the interfaces is done * after the construction of the editor class. @@ -70,9 +79,9 @@ public: virtual nsresult GetDomInterface(nsIDOMDocument **aDomInterface); - virtual nsresult SetProperties(PROPERTIES aProperty); + virtual nsresult SetProperties(Properties aProperties); - virtual nsresult GetProperties(PROPERTIES **); + virtual nsresult GetProperties(Properties **aProperties); virtual nsresult SetAttribute(nsIDOMElement * aElement, const nsString& aAttribute, @@ -85,11 +94,15 @@ public: virtual nsresult RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute); - virtual nsresult InsertString(nsString *aString); - - virtual nsresult Commit(PRBool aCtrlKey); + virtual nsresult Do(nsITransaction *aTxn); + virtual nsresult Undo(PRUint32 aCount); + virtual nsresult Redo(PRUint32 aCount); + + virtual nsresult BeginUpdate(); + + virtual nsresult EndUpdate(); /*END nsIEditor interfaces*/ @@ -113,12 +126,6 @@ public: /*BEGIN private methods used by the implementations of the above functions*/ - /** AppendText is a private method that accepts a pointer to a string - * and will append it to the current node. this will be depricated - * @param nsString *aStr is the pointer to the valid string - */ - nsresult AppendText(nsString *aStr); - /** GetCurrentNode ADDREFFS and will get the current node from selection. * now it simply returns the first node in the dom * @param nsIDOMNode **aNode is the return location of the dom node @@ -142,33 +149,6 @@ public: */ nsresult GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDOMNode **aResult); - /** ExecuteTransaction fires a transaction. 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 used. - * Otherwise, the transaction is just executed directly. - * - * @param aTxn the transaction to execute - */ - nsresult ExecuteTransaction(nsITransaction *aTxn); - - /** Undo reverses the effects of the last ExecuteTransaction 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 undo and the result of - * that undo is returned. - * Otherwise, the Undo request is ignored. - * - */ - nsresult Undo(); - - /** 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 - * that redo is returned. - * Otherwise, the Redo request is ignored. - * - */ - nsresult Redo(); - nsresult CreateElement(const nsString& aTag, nsIDOMNode * aParent, PRInt32 aPosition); @@ -178,17 +158,15 @@ public: nsresult DeleteSelection(); - nsresult InsertText(nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert); + nsresult InsertText(const nsString& aStringToInsert); nsresult DeleteText(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aLength); - static nsresult SplitNode(nsIDOMNode * aNode, + static nsresult SplitNode(nsIDOMNode * aExistingRightNode, PRInt32 aOffset, - nsIDOMNode * aNewNode, + nsIDOMNode * aNewLeftNode, nsIDOMNode * aParent); static nsresult JoinNodes(nsIDOMNode * aNodeToKeep, @@ -198,6 +176,10 @@ public: nsresult Delete(PRBool aForward, PRUint32 aCount); + virtual nsresult InsertString(nsString *aString); + + virtual nsresult Commit(PRBool aCtrlKey); + /*END private methods of nsEditor*/ }; @@ -207,3 +189,6 @@ factory method(s) */ nsresult NS_MakeEditorLoader(nsIContextLoader **aResult); + + +#endif \ No newline at end of file diff --git a/mozilla/editor/core/editorInterfaces.cpp b/mozilla/editor/core/editorInterfaces.cpp index a9d5a1abfb9..baebf408a85 100644 --- a/mozilla/editor/core/editorInterfaces.cpp +++ b/mozilla/editor/core/editorInterfaces.cpp @@ -19,7 +19,6 @@ #include "editor.h" #include "CreateElementTxn.h" -#include "SplitElementTxn.h" #include "nsIDOMDocument.h" #include "nsIDOMElement.h" @@ -178,7 +177,7 @@ nsEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) } else { // XXX: delete the first P we find - nsString pTag("P"); + nsAutoString pTag("P"); nsCOMPtr currentNode; nsCOMPtr parentNode; nsCOMPtr element; @@ -199,25 +198,12 @@ nsEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent) { // XXX Replace with x-platform NS-virtkeycode transform. if (NS_OK == GetCharFromKeyCode(keyCode, isShift, & character)) { - nsString key; + nsAutoString key; key += character; if (!isShift) { key.ToLowerCase(); } - - // XXX: for now, just grab the first text node - nsCOMPtr currentNode; - nsCOMPtr textNode; - nsCOMPtr text; - if (NS_SUCCEEDED(mEditor->GetCurrentNode(getter_AddRefs(currentNode))) && - NS_SUCCEEDED(mEditor->GetFirstTextNode(currentNode,getter_AddRefs(textNode))) && - NS_SUCCEEDED(textNode->QueryInterface(kIDOMCharacterDataIID, getter_AddRefs(text)))) - { - // XXX: for now, just append the text - PRUint32 offset; - text->GetLength(&offset); - mEditor->InsertText(text, offset, key); - } + mEditor->InsertText(key); } } break; @@ -244,6 +230,10 @@ nsEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) return NS_OK; } +/* these includes are for debug only. this module should never instantiate it's own transactions */ +#include "SplitElementTxn.h" +#include "TransactionFactory.h" +static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID); nsresult nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProcessed) { @@ -265,7 +255,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces if (PR_TRUE==ctrlKey) { if (nsnull!=mEditor) - mEditor->Undo(); + mEditor->Undo(1); } aProcessed=PR_TRUE; break; @@ -275,7 +265,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces if (PR_TRUE==ctrlKey) { if (nsnull!=mEditor) - mEditor->Redo(); + mEditor->Redo(1); } aProcessed=PR_TRUE; break; @@ -283,17 +273,27 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces // hard-coded split node test: works on first

in the document case nsIDOMEvent::VK_S: { - nsString pTag("P"); + nsAutoString pTag("P"); nsCOMPtr currentNode; nsCOMPtr element; if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, pTag, getter_AddRefs(currentNode)))) { + nsresult result; SplitElementTxn *txn; if (PR_FALSE==isShift) // split the element so there are 0 children in the first half - txn = new SplitElementTxn(mEditor, currentNode, -1); + { + result = TransactionFactory::GetNewTransaction(kSplitElementTxnIID, (EditTxn **)&txn); + if (txn) + txn->Init(currentNode, -1); + } else // split the element so there are 2 children in the first half - txn = new SplitElementTxn(mEditor, currentNode, 1); - mEditor->ExecuteTransaction(txn); + { + result = TransactionFactory::GetNewTransaction(kSplitElementTxnIID, (EditTxn **)&txn); + if (txn) + txn->Init(currentNode, 1); + } + if (txn) + mEditor->Do(txn); } } aProcessed=PR_TRUE; @@ -305,10 +305,10 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces { //XXX: should be from a factory //XXX: should manage the refcount of txn - nsString attribute("width"); - nsString value("400"); + nsAutoString attribute("width"); + nsAutoString value("400"); - nsString tableTag("TABLE"); + nsAutoString tableTag("TABLE"); nsCOMPtr currentNode; nsCOMPtr element; if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, tableTag, getter_AddRefs(currentNode)))) @@ -331,11 +331,11 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces nsresult result; //XXX: should be from a factory //XXX: should manage the refcount of txn - nsString attribute("src"); - nsString value("resource:/res/samples/raptor.jpg"); + nsAutoString attribute("src"); + nsAutoString value("resource:/res/samples/raptor.jpg"); - nsString imgTag("HR"); - nsString bodyTag("BODY"); + nsAutoString imgTag("HR"); + nsAutoString bodyTag("BODY"); nsCOMPtr currentNode; result = mEditor->GetFirstNodeOfType(nsnull, bodyTag, getter_AddRefs(currentNode)); if (NS_SUCCEEDED(result)) @@ -356,7 +356,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces { ChangeAttributeTxn *txn; txn = new ChangeAttributeTxn(mEditor, element, attribute, value, PR_FALSE); - mEditor->ExecuteTransaction(txn); + mEditor->Do(txn); } } */ @@ -523,3 +523,4 @@ NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsEditor *a return it->QueryInterface(kIDOMEventListenerIID, (void **) aInstancePtrResult); } + diff --git a/mozilla/editor/core/makefile.win b/mozilla/editor/core/makefile.win index 0eb893bbaf5..cad929eda1f 100644 --- a/mozilla/editor/core/makefile.win +++ b/mozilla/editor/core/makefile.win @@ -25,6 +25,7 @@ CPPSRCS = \ editorInterfaces.cpp \ nsEditFactory.cpp \ EditTxn.cpp \ + EditAggregateTxn.cpp \ ChangeAttributeTxn.cpp \ InsertTextTxn.cpp \ DeleteTextTxn.cpp \ @@ -32,6 +33,8 @@ CPPSRCS = \ DeleteElementTxn.cpp \ DeleteRangeTxn.cpp \ SplitElementTxn.cpp \ + JoinElementTxn.cpp \ + TransactionFactory.cpp \ $(NULL) CPP_OBJS = \ @@ -39,6 +42,7 @@ CPP_OBJS = \ .\$(OBJDIR)\nsEditFactory.obj \ .\$(OBJDIR)\editorInterfaces.obj \ .\$(OBJDIR)\EditTxn.obj \ + .\$(OBJDIR)\EditAggregateTxn.obj \ .\$(OBJDIR)\ChangeAttributeTxn.obj \ .\$(OBJDIR)\InsertTextTxn.obj \ .\$(OBJDIR)\DeleteTextTxn.obj \ @@ -46,6 +50,8 @@ CPP_OBJS = \ .\$(OBJDIR)\DeleteElementTxn.obj \ .\$(OBJDIR)\DeleteRangeTxn.obj \ .\$(OBJDIR)\SplitElementTxn.obj \ + .\$(OBJDIR)\JoinElementTxn.obj \ + .\$(OBJDIR)\TransactionFactory.obj \ $(NULL) MODULE=editor diff --git a/mozilla/editor/libeditor/base/ChangeAttributeTxn.cpp b/mozilla/editor/libeditor/base/ChangeAttributeTxn.cpp index 234e9031c84..e3cc0e8fb0b 100644 --- a/mozilla/editor/libeditor/base/ChangeAttributeTxn.cpp +++ b/mozilla/editor/libeditor/base/ChangeAttributeTxn.cpp @@ -17,23 +17,33 @@ */ #include "ChangeAttributeTxn.h" -#include "editor.h" #include "nsIDOMElement.h" +#include "editor.h" -// note that aEditor is not refcounted -ChangeAttributeTxn::ChangeAttributeTxn(nsEditor *aEditor, - nsIDOMElement *aElement, - const nsString& aAttribute, - const nsString& aValue, - PRBool aRemoveAttribute) - : EditTxn(aEditor) +ChangeAttributeTxn::ChangeAttributeTxn() + : EditTxn() { - mElement = aElement; - mAttribute = aAttribute; - mValue = aValue; - mRemoveAttribute = aRemoveAttribute; - mAttributeWasSet=PR_FALSE; - mUndoValue=""; +} + +nsresult ChangeAttributeTxn::Init(nsIEditor *aEditor, + nsIDOMElement *aElement, + const nsString& aAttribute, + const nsString& aValue, + PRBool aRemoveAttribute) +{ + if (nsnull!=aEditor && nsnull!=aElement) + { + mEditor = aEditor; + mElement = aElement; + mAttribute = aAttribute; + mValue = aValue; + mRemoveAttribute = aRemoveAttribute; + mAttributeWasSet=PR_FALSE; + mUndoValue=""; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; } nsresult ChangeAttributeTxn::Do(void) diff --git a/mozilla/editor/libeditor/base/ChangeAttributeTxn.h b/mozilla/editor/libeditor/base/ChangeAttributeTxn.h index 689d991f3c0..48bded0e116 100644 --- a/mozilla/editor/libeditor/base/ChangeAttributeTxn.h +++ b/mozilla/editor/libeditor/base/ChangeAttributeTxn.h @@ -20,8 +20,14 @@ #define ChangeAttributeTxn_h__ #include "EditTxn.h" +#include "nsCOMPtr.h" +#include "nsIDOMElement.h" +#include "nsIEditor.h" -class nsIDOMElement; +#define CHANGE_ATTRIBUTE_TXN_IID \ +{/* 97818860-ac48-11d2-86d8-000064657374 */ \ +0x97818860, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that changes an attribute of a content node. @@ -31,11 +37,16 @@ class ChangeAttributeTxn : public EditTxn { public: - ChangeAttributeTxn(nsEditor *aEditor, - nsIDOMElement *aElement, - const nsString& aAttribute, - const nsString& aValue, - PRBool aRemoveAttribute); + virtual nsresult Init(nsIEditor *aEditor, + nsIDOMElement *aElement, + const nsString& aAttribute, + const nsString& aValue, + PRBool aRemoveAttribute); + +private: + ChangeAttributeTxn(); + +public: virtual nsresult Do(void); @@ -54,9 +65,12 @@ public: virtual nsresult GetRedoString(nsString **aString); protected: + + /** the editor that created this transaction */ + nsCOMPtr mEditor; /** the element to operate upon */ - nsIDOMElement *mElement; + nsCOMPtr mElement; /** the attribute to change */ nsString mAttribute; @@ -72,6 +86,8 @@ protected: /** PR_TRUE if the operation is to remove mAttribute from mElement */ PRBool mRemoveAttribute; + + friend class TransactionFactory; }; #endif diff --git a/mozilla/editor/libeditor/base/CreateElementTxn.cpp b/mozilla/editor/libeditor/base/CreateElementTxn.cpp index c62adf8b423..44f1b69170a 100644 --- a/mozilla/editor/libeditor/base/CreateElementTxn.cpp +++ b/mozilla/editor/libeditor/base/CreateElementTxn.cpp @@ -17,52 +17,50 @@ */ #include "CreateElementTxn.h" -#include "editor.h" -#include "nsIDOMNode.h" #include "nsIDOMNodeList.h" -#include "nsIDOMDocument.h" -#include "nsIDOMElement.h" -// note that aEditor is not refcounted -CreateElementTxn::CreateElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - const nsString& aTag, - nsIDOMNode *aParent, - PRUint32 aOffsetInParent) - : EditTxn(aEditor) +CreateElementTxn::CreateElementTxn() + : EditTxn() { - mDoc = aDoc; - NS_ADDREF(mDoc); - mTag = aTag; - mParent = aParent; - NS_ADDREF(mParent); - mOffsetInParent = aOffsetInParent; - mNewNode = nsnull; - mRefNode = nsnull; } +nsresult CreateElementTxn::Init(nsIDOMDocument *aDoc, + const nsString& aTag, + nsIDOMNode *aParent, + PRUint32 aOffsetInParent) +{ + if ((nsnull!=aDoc) && (nsnull!=aParent)) + { + mDoc = aDoc; + mTag = aTag; + mParent = aParent; + mOffsetInParent = aOffsetInParent; + mNewNode = nsnull; + mRefNode = nsnull; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; +} + + CreateElementTxn::~CreateElementTxn() { - NS_IF_RELEASE(mDoc); - NS_IF_RELEASE(mParent); - NS_IF_RELEASE(mNewNode); } nsresult CreateElementTxn::Do(void) { // create a new node - nsresult result = mDoc->CreateElement(mTag, &mNewNode); - NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); + nsresult result = mDoc->CreateElement(mTag, getter_AddRefs(mNewNode)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); - if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) + if ((NS_SUCCEEDED(result)) && (mNewNode)) { // insert the new node - nsIDOMNode *resultNode=nsnull; + nsCOMPtr resultNode; if (CreateElementTxn::eAppend==mOffsetInParent) { - result = mParent->AppendChild(mNewNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); // this object already holds a ref from CreateElement + result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode)); } else { @@ -70,12 +68,10 @@ nsresult CreateElementTxn::Do(void) result = mParent->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { - result = childNodes->Item(mOffsetInParent, &mRefNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=mRefNode)) + result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); + if ((NS_SUCCEEDED(result)) && (mRefNode)) { - result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); // this object already holds a ref from CreateElement + result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); } } } @@ -85,20 +81,16 @@ nsresult CreateElementTxn::Do(void) nsresult CreateElementTxn::Undo(void) { - nsIDOMNode *resultNode=nsnull; - nsresult result = mParent->RemoveChild(mNewNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); + nsCOMPtr resultNode; + nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); return result; } nsresult CreateElementTxn::Redo(void) { - nsIDOMNode *resultNode=nsnull; - nsresult result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); - if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) - NS_RELEASE(resultNode); - return result; + nsCOMPtr resultNode; + nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + return result; } nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient) diff --git a/mozilla/editor/libeditor/base/CreateElementTxn.h b/mozilla/editor/libeditor/base/CreateElementTxn.h index 269de9c3f5a..bbe1c4a4421 100644 --- a/mozilla/editor/libeditor/base/CreateElementTxn.h +++ b/mozilla/editor/libeditor/base/CreateElementTxn.h @@ -20,10 +20,15 @@ #define CreateElementTxn_h__ #include "EditTxn.h" +#include "nsIDOMDocument.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsCOMPtr.h" -class nsIDOMDocument; -class nsIDOMNode; -class nsIDOMElement; +#define CREATE_ELEMENT_TXN_IID \ +{/* 7a6393c0-ac48-11d2-86d8-000064657374 */ \ +0x7a6393c0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that creates a new node in the content tree. @@ -34,11 +39,15 @@ public: enum { eAppend=-1 }; - CreateElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - const nsString& aTag, - nsIDOMNode *aParent, - PRUint32 aOffsetInParent); + virtual nsresult Init(nsIDOMDocument *aDoc, + const nsString& aTag, + nsIDOMNode *aParent, + PRUint32 aOffsetInParent); + +private: + CreateElementTxn(); + +public: virtual ~CreateElementTxn(); @@ -61,22 +70,24 @@ public: protected: /** the document into which the new node will be inserted */ - nsIDOMDocument *mDoc; + nsCOMPtr mDoc; /** the tag (mapping to object type) for the new element */ nsString mTag; /** the node into which the new node will be inserted */ - nsIDOMNode *mParent; + nsCOMPtr mParent; /** the index in mParent for the new node */ PRUint32 mOffsetInParent; /** the new node to insert */ - nsIDOMElement *mNewNode; + nsCOMPtr mNewNode; /** the node we will insert mNewNode before. We compute this ourselves. */ - nsIDOMNode *mRefNode; + nsCOMPtr mRefNode; + + friend class TransactionFactory; }; diff --git a/mozilla/editor/libeditor/base/DeleteElementTxn.cpp b/mozilla/editor/libeditor/base/DeleteElementTxn.cpp index 85ca3462ebb..5b55f78a54f 100644 --- a/mozilla/editor/libeditor/base/DeleteElementTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteElementTxn.cpp @@ -17,30 +17,32 @@ */ #include "DeleteElementTxn.h" -#include "editor.h" -#include "nsIDOMDocument.h" +#ifdef NS_DEBUG #include "nsIDOMElement.h" +#endif -// note that aEditor is not refcounted -DeleteElementTxn::DeleteElementTxn(nsEditor * aEditor, - nsIDOMDocument *aDoc, - nsIDOMNode * aElement, - nsIDOMNode * aParent) - : EditTxn(aEditor) + +DeleteElementTxn::DeleteElementTxn() + : EditTxn() { - mDoc = aDoc; - NS_ADDREF(mDoc); - mElement = aElement; - NS_ADDREF(mElement); - mParent = aParent; - NS_ADDREF(mParent); } +nsresult DeleteElementTxn::Init(nsIDOMNode *aElement, + nsIDOMNode *aParent) +{ + if ((nsnull!=aElement) && (nsnull!=aParent)) + { + mElement = aElement; + mParent = aParent; + return NS_OK; + } + else + return NS_ERROR_NULL_POINTER; +} + + DeleteElementTxn::~DeleteElementTxn() { - NS_IF_RELEASE(mDoc); - NS_IF_RELEASE(mParent); - NS_IF_RELEASE(mElement); } nsresult DeleteElementTxn::Do(void) @@ -48,11 +50,32 @@ nsresult DeleteElementTxn::Do(void) if (!mParent || !mElement) return NS_ERROR_NULL_POINTER; #ifdef NS_DEBUG + // begin debug output + nsCOMPtr element=mElement; + nsAutoString elementTag="text node"; + if (element) + element->GetTagName(elementTag); + nsCOMPtr parentElement=mParent; + nsAutoString parentElementTag="text node"; + if (parentElement) + parentElement->GetTagName(parentElementTag); + char *c, *p; + c = elementTag.ToNewCString(); + p = parentElementTag.ToNewCString(); + if (c&&p) + { + printf("DeleteElementTxn: deleting child %s from parent %s\n", c, p); + delete [] c; + delete [] p; + } + // end debug output + // begin sanity check 1: parent-child relationship nsresult testResult; nsCOMPtr parentNode; testResult = mElement->GetParentNode(getter_AddRefs(parentNode)); NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent"); NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() "); + // end sanity check 1. #endif // remember which child mElement was (by remembering which child was next) diff --git a/mozilla/editor/libeditor/base/DeleteElementTxn.h b/mozilla/editor/libeditor/base/DeleteElementTxn.h index dce2b04dcda..9cf3469f918 100644 --- a/mozilla/editor/libeditor/base/DeleteElementTxn.h +++ b/mozilla/editor/libeditor/base/DeleteElementTxn.h @@ -23,8 +23,10 @@ #include "nsIDOMNode.h" #include "nsCOMPtr.h" -class nsIDOMDocument; -class nsIDOMElement; +#define DELETE_ELEMENT_TXN_IID \ +{/* 6fd77770-ac49-11d2-86d8-000064657374 */ \ +0x6fd77770, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that deletes a single element @@ -33,10 +35,13 @@ class DeleteElementTxn : public EditTxn { public: - DeleteElementTxn(nsEditor *aEditor, - nsIDOMDocument *aDoc, - nsIDOMNode *aElement, - nsIDOMNode *aParent); + virtual nsresult Init(nsIDOMNode *aElement, + nsIDOMNode *aParent); + +private: + DeleteElementTxn(); + +public: virtual ~DeleteElementTxn(); @@ -58,14 +63,11 @@ public: protected: - /** the document into which the new node will be inserted */ - nsIDOMDocument *mDoc; - /** the element to delete */ - nsIDOMNode *mElement; + nsCOMPtr mElement; /** the node into which the new node will be inserted */ - nsIDOMNode *mParent; + nsCOMPtr mParent; /** the index in mParent for the new node */ PRUint32 mOffsetInParent; @@ -73,6 +75,8 @@ protected: /** the node we will insert mNewNode before. We compute this ourselves. */ nsCOMPtr mRefNode; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp b/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp index 84a1b397e4d..37a6e286434 100644 --- a/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp @@ -17,18 +17,73 @@ */ #include "DeleteRangeTxn.h" -#include "editor.h" #include "nsIDOMRange.h" +#include "nsIDOMCharacterData.h" +#include "nsIDOMNodeList.h" +#include "DeleteTextTxn.h" +#include "DeleteElementTxn.h" +#include "TransactionFactory.h" +#include "nsISupportsArray.h" + +static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID); +static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); + // note that aEditor is not refcounted -DeleteRangeTxn::DeleteRangeTxn(nsEditor *aEditor, - nsIDOMRange *aRange) - : EditTxn(aEditor) +DeleteRangeTxn::DeleteRangeTxn() + : EditAggregateTxn() { - aRange->GetStartParent(getter_AddRefs(mStartParent)); - aRange->GetEndParent(getter_AddRefs(mEndParent)); - aRange->GetStartOffset(&mStartOffset); - aRange->GetEndOffset(&mEndOffset); +} + +nsresult DeleteRangeTxn::Init(nsIDOMRange *aRange) +{ + if (nsnull!=aRange) + { + nsresult result = aRange->GetStartParent(getter_AddRefs(mStartParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed."); + result = aRange->GetEndParent(getter_AddRefs(mEndParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed."); + result = aRange->GetStartOffset(&mStartOffset); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed."); + result = aRange->GetEndOffset(&mEndOffset); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed."); + result = aRange->GetCommonParent(getter_AddRefs(mCommonParent)); + NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed."); + +#ifdef NS_DEBUG + PRUint32 count; + nsCOMPtr textNode; + textNode = mStartParent; + if (textNode) + textNode->GetLength(&count); + else + { + nsCOMPtr children; + result = mStartParent->GetChildNodes(getter_AddRefs(children)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad start child list"); + children->GetLength(&count); + } + NS_ASSERTION(mStartOffsetGetLength(&count); + else + { + nsCOMPtr children; + result = mEndParent->GetChildNodes(getter_AddRefs(children)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad end child list"); + children->GetLength(&count); + } + NS_ASSERTION(mEndOffset textNode; + textNode = aStartParent; // this uses implicit nsCOMPtr QI + if (textNode) + { // if the node is a text node, then delete text content + DeleteTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(textNode, aStartOffset, (aEndOffset-aStartOffset)+1); + AppendChild(txn); + } + } + else + { + PRUint32 childCount; + nsCOMPtr children; + result = aStartParent->GetChildNodes(getter_AddRefs(children)); + if ((NS_SUCCEEDED(result)) && children) + { + children->GetLength(&childCount); + NS_ASSERTION(aEndOffset child; + result = children->Item(i, getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, aStartParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + } + } + return result; +} + +nsresult DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, + PRUint32 aOffset, + nsIEditor::Direction aDir) +{ + nsresult result; + // see what kind of node we have + nsCOMPtr textNode; + textNode = aParent; // this uses implicit nsCOMPtr QI + if (textNode) + { // if the node is a text node, then delete text content + PRUint32 start, numToDelete; + if (nsIEditor::eLTR==aDir) + { + start=aOffset; + textNode->GetLength(&numToDelete); + numToDelete -= (aOffset+1); + } + else + { + start=0; + numToDelete=aOffset; + } + DeleteTextTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(textNode, start, numToDelete); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + else + { // we have an interior node, so delete some of its children + if (nsIEditor::eLTR==aDir) + { // delete from aOffset to end + PRUint32 childCount; + nsCOMPtr children; + result = aParent->GetChildNodes(getter_AddRefs(children)); + if ((NS_SUCCEEDED(result)) && children) + { + children->GetLength(&childCount); + PRUint32 i; + for (i=aOffset; i child; + result = children->Item(i, getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, aParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + } + } + else + { // delete from 0 to aOffset + nsCOMPtr child; + result = aParent->GetFirstChild(getter_AddRefs(child)); + for (PRUint32 i=0; iInit(child, aParent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + nsCOMPtr temp = child; + result = temp->GetNextSibling(getter_AddRefs(child)); + } + } + } + + return result; +} + +nsresult DeleteRangeTxn::CreateTxnsToDeleteNodesBetween(nsIDOMNode *aCommonParent, + nsIDOMNode *aFirstChild, + nsIDOMNode *aLastChild) +{ + nsresult result; + PRBool needToProcessLastChild=PR_TRUE; // set to false if we discover we can delete all required nodes by just walking up aFirstChild's parent list + nsCOMPtr parent; // the current parent in the iteration up the ancestors + nsCOMPtr child; // the current child of parent + nsISupportsArray *ancestorList; // the ancestorList of the other endpoint, used to gate deletion + NS_NewISupportsArray(&ancestorList); + if (nsnull==ancestorList) + return NS_ERROR_NULL_POINTER; + + // Walk up the parent list of aFirstChild to aCommonParent, + // deleting all siblings to the right of the ancestors of aFirstChild. + BuildAncestorList(aLastChild, ancestorList); + child = aFirstChild; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && parent) + { + while ((NS_SUCCEEDED(result)) && child) + { // this loop starts with the first sibling of an ancestor of aFirstChild + nsCOMPtr temp = child; + result = temp->GetNextSibling(getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + if (child==aLastChild) + { // aFirstChild and aLastChild have the same parent, and we've reached aLastChild + needToProcessLastChild = PR_FALSE; + break; + } + // test if child is an ancestor of the other node. If it is, don't process this parent anymore + PRInt32 index; + index = ancestorList->IndexOf((nsISupports*)child); + if (-1!=index) + break; + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, parent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + if (parent==aCommonParent) + break; + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + + // Walk up the parent list of aLastChild to aCommonParent, + // deleting all siblings to the left of the ancestors of aLastChild. + BuildAncestorList(aFirstChild, ancestorList); + if (PR_TRUE==needToProcessLastChild) + { + child = aLastChild; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && parent) + { + while ((NS_SUCCEEDED(result)) && child) + { // this loop starts with the first sibling of an ancestor of aFirstChild + nsCOMPtr temp = child; + result = temp->GetPreviousSibling(getter_AddRefs(child)); + if ((NS_SUCCEEDED(result)) && child) + { + // test if child is an ancestor of the other node. If it is, don't process this parent anymore + PRInt32 index; + index = ancestorList->IndexOf((nsISupports*)child); + if (-1!=index) + break; + DeleteElementTxn *txn; + result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn); + if (nsnull!=txn) + { + txn->Init(child, parent); + AppendChild(txn); + } + else + return NS_ERROR_NULL_POINTER; + } + } + if (parent==aCommonParent) + break; + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + } + NS_RELEASE(ancestorList); + return result; +} + +nsresult DeleteRangeTxn::BuildAncestorList(nsIDOMNode *aNode, nsISupportsArray *aList) +{ + nsresult result=NS_OK; + if (nsnull!=aNode && nsnull!=aList) + { + aList->Clear(); + nsCOMPtr parent; + nsCOMPtr child = aNode; + result = child->GetParentNode(getter_AddRefs(parent)); + while ((NS_SUCCEEDED(result)) && child && parent) + { + nsISupports * parentAsISupports; + parent->QueryInterface(nsISupports::IID(), (void **)&parentAsISupports); + aList->AppendElement(parentAsISupports); + child = parent; + nsCOMPtr temp=parent; + result = temp->GetParentNode(getter_AddRefs(parent)); + } + } + else + result = NS_ERROR_NULL_POINTER; + + return result; +} + + diff --git a/mozilla/editor/libeditor/base/DeleteRangeTxn.h b/mozilla/editor/libeditor/base/DeleteRangeTxn.h index 675049f550a..09af0e639c6 100644 --- a/mozilla/editor/libeditor/base/DeleteRangeTxn.h +++ b/mozilla/editor/libeditor/base/DeleteRangeTxn.h @@ -19,22 +19,33 @@ #ifndef DeleteRangeTxn_h__ #define DeleteRangeTxn_h__ -#include "EditTxn.h" +#include "EditAggregateTxn.h" #include "nsIDOMNode.h" +#include "nsIEditor.h" #include "nsCOMPtr.h" class nsIDOMDocument; class nsIDOMRange; +class nsISupportsArray; + +#define DELETE_RANGE_TXN_IID \ +{/* 5ec6b260-ac49-11d2-86d8-000064657374 */ \ +0x5ec6b260, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that deletes an entire range in the content tree */ -class DeleteRangeTxn : public EditTxn +class DeleteRangeTxn : public EditAggregateTxn { public: - DeleteRangeTxn(nsEditor *aEditor, - nsIDOMRange *aRange); + virtual nsresult Init(nsIDOMRange *aRange); + +private: + DeleteRangeTxn(); + +public: virtual ~DeleteRangeTxn(); @@ -54,6 +65,23 @@ public: virtual nsresult GetRedoString(nsString **aString); +protected: + + virtual nsresult CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, + PRUint32 aStartOffset, + PRUint32 aEndOffset); + + virtual nsresult CreateTxnsToDeleteNodesBetween(nsIDOMNode *aParent, + nsIDOMNode *aFirstChild, + nsIDOMNode *aLastChild); + + virtual nsresult CreateTxnsToDeleteContent(nsIDOMNode *aParent, + PRUint32 aOffset, + nsIEditor::Direction aDir); + + virtual nsresult BuildAncestorList(nsIDOMNode *aNode, + nsISupportsArray *aList); + protected: /** p1 in the range */ @@ -65,9 +93,14 @@ protected: /** p2 in the range */ nsCOMPtr mEndParent; + /** the closest common parent of p1 and p2 */ + nsCOMPtr mCommonParent; + /** p2 offset */ PRInt32 mEndOffset; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/libeditor/base/DeleteTextTxn.cpp b/mozilla/editor/libeditor/base/DeleteTextTxn.cpp index 09df4b2b0ab..eda056e3a3a 100644 --- a/mozilla/editor/libeditor/base/DeleteTextTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteTextTxn.cpp @@ -17,20 +17,23 @@ */ #include "DeleteTextTxn.h" -#include "editor.h" #include "nsIDOMCharacterData.h" -// note that aEditor is not refcounted -DeleteTextTxn::DeleteTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, + +DeleteTextTxn::DeleteTextTxn() + : EditTxn() +{ +} + +nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aNumCharsToDelete) - : EditTxn(aEditor) { mElement = aElement; mOffset = aOffset; mNumCharsToDelete = aNumCharsToDelete; mDeletedText = ""; + return NS_OK; } nsresult DeleteTextTxn::Do(void) diff --git a/mozilla/editor/libeditor/base/DeleteTextTxn.h b/mozilla/editor/libeditor/base/DeleteTextTxn.h index 73d2e35ae08..ad2fd0de334 100644 --- a/mozilla/editor/libeditor/base/DeleteTextTxn.h +++ b/mozilla/editor/libeditor/base/DeleteTextTxn.h @@ -20,8 +20,13 @@ #define DeleteTextTxn_h__ #include "EditTxn.h" +#include "nsIDOMCharacterData.h" +#include "nsCOMPtr.h" -class nsIDOMCharacterData; +#define DELETE_TEXT_TXN_IID \ +{/* 4d3a2720-ac49-11d2-86d8-000064657374 */ \ +0x4d3a2720, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that changes an attribute of a content node. @@ -31,11 +36,15 @@ class DeleteTextTxn : public EditTxn { public: - DeleteTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, - PRUint32 aOffset, - PRUint32 aNumCharsToDelete); + virtual nsresult Init(nsIDOMCharacterData *aElement, + PRUint32 aOffset, + PRUint32 aNumCharsToDelete); +private: + DeleteTextTxn(); + +public: + virtual nsresult Do(void); virtual nsresult Undo(void); @@ -53,7 +62,7 @@ public: protected: /** the text element to operate upon */ - nsIDOMCharacterData *mElement; + nsCOMPtr mElement; /** the offset into mElement where the deletion is to take place */ PRUint32 mOffset; @@ -64,6 +73,8 @@ protected: /** the text that was deleted */ nsString mDeletedText; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/libeditor/base/EditAggregateTxn.cpp b/mozilla/editor/libeditor/base/EditAggregateTxn.cpp new file mode 100644 index 00000000000..e07c6d3f8c6 --- /dev/null +++ b/mozilla/editor/libeditor/base/EditAggregateTxn.cpp @@ -0,0 +1,145 @@ +/* -*- 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 "EditAggregateTxn.h" +#include "nsCOMPtr.h" +#include "nsIDOMNode.h" +#include "nsVoidArray.h" + +EditAggregateTxn::EditAggregateTxn() + : EditTxn() +{ + mChildren = new nsVoidArray(); +} + +EditAggregateTxn::~EditAggregateTxn() +{ + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + NS_IF_RELEASE(txn); + } + delete mChildren; + } +} + +nsresult EditAggregateTxn::Do(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + result = txn->Do(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::Undo(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + // undo goes through children backwards + for (i=count-1; i>=0; i--) + { + EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i)); + result = txn->Undo(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::Redo(void) +{ + nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list + if (nsnull!=mChildren) + { + PRInt32 i; + PRInt32 count = mChildren->Count(); + for (i=0; iElementAt(i)); + result = txn->Redo(); + if (NS_FAILED(result)) + break; + } + } + return result; +} + +nsresult EditAggregateTxn::GetIsTransient(PRBool *aIsTransient) +{ + if (nsnull!=aIsTransient) + *aIsTransient = PR_TRUE; + return NS_OK; +} + +nsresult EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) +{ + return NS_OK; +} + +nsresult EditAggregateTxn::Write(nsIOutputStream *aOutputStream) +{ + return NS_OK; +} + +nsresult EditAggregateTxn::GetUndoString(nsString **aString) +{ + if (nsnull!=aString) + *aString=nsnull; + return NS_OK; +} + +nsresult EditAggregateTxn::GetRedoString(nsString **aString) +{ + if (nsnull!=aString) + *aString=nsnull; + return NS_OK; +} + +nsresult EditAggregateTxn::AppendChild(EditTxn *aTxn) +{ + if ((nsnull!=mChildren) && (nsnull!=aTxn)) + { + mChildren->AppendElement(aTxn); + return NS_OK; + } + return NS_ERROR_NULL_POINTER; +} + + + + + + diff --git a/mozilla/editor/libeditor/base/EditAggregateTxn.h b/mozilla/editor/libeditor/base/EditAggregateTxn.h new file mode 100644 index 00000000000..9830a1d0656 --- /dev/null +++ b/mozilla/editor/libeditor/base/EditAggregateTxn.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 EditAggregateTxn_h__ +#define EditAggregateTxn_h__ + +#include "EditTxn.h" + +#define EDIT_AGGREGATE_TXN_IID \ +{/* 345921a0-ac49-11d2-86d8-000064657374 */ \ +0x345921a0, 0xac49, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + +class nsVoidArray; + +/** + * base class for all document editing transactions that require aggregation. + * provides a list of child transactions. + */ +class EditAggregateTxn : public EditTxn +{ +public: + + EditAggregateTxn(); + + virtual ~EditAggregateTxn(); + + virtual nsresult Do(void); + + virtual nsresult Undo(void); + + virtual nsresult Redo(void); + + virtual nsresult GetIsTransient(PRBool *aIsTransient); + + virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction); + + virtual nsresult Write(nsIOutputStream *aOutputStream); + + virtual nsresult GetUndoString(nsString **aString); + + virtual nsresult GetRedoString(nsString **aString); + + virtual nsresult AppendChild(EditTxn *aTxn); + +protected: + + nsVoidArray *mChildren; + +}; + +#endif \ No newline at end of file diff --git a/mozilla/editor/libeditor/base/EditTxn.cpp b/mozilla/editor/libeditor/base/EditTxn.cpp index 768bef6604e..d17b6d2583f 100644 --- a/mozilla/editor/libeditor/base/EditTxn.cpp +++ b/mozilla/editor/libeditor/base/EditTxn.cpp @@ -28,10 +28,8 @@ NS_IMPL_ADDREF(EditTxn) NS_IMPL_RELEASE(EditTxn) // note that aEditor is not refcounted -EditTxn::EditTxn(nsEditor *aEditor) +EditTxn::EditTxn() { - NS_ASSERTION(nsnull!=aEditor, "null aEditor arg to EditTxn constructor"); - mEditor = aEditor; } nsresult EditTxn::Do(void) diff --git a/mozilla/editor/libeditor/base/EditTxn.h b/mozilla/editor/libeditor/base/EditTxn.h index afd99fbed46..97381e64dc5 100644 --- a/mozilla/editor/libeditor/base/EditTxn.h +++ b/mozilla/editor/libeditor/base/EditTxn.h @@ -20,13 +20,17 @@ #define EditTxn_h__ #include "nsITransaction.h" -class nsEditor; class nsIDOMNode; +#define EDIT_TXN_IID \ +{/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \ +0xc5ea31b0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + /** * base class for all document editing transactions. - * provides access to the nsEditor that created this transaction. + * provides default concrete behavior for all nsITransaction methods. */ class EditTxn : public nsITransaction { @@ -34,7 +38,7 @@ public: NS_DECL_ISUPPORTS - EditTxn(nsEditor *aEditor); + EditTxn(); virtual nsresult Do(void); @@ -52,10 +56,6 @@ public: virtual nsresult GetRedoString(nsString **aString); -protected: - - nsEditor *mEditor; - }; #endif \ No newline at end of file diff --git a/mozilla/editor/libeditor/base/InsertTextTxn.cpp b/mozilla/editor/libeditor/base/InsertTextTxn.cpp index 4d078cc193f..220c9e46916 100644 --- a/mozilla/editor/libeditor/base/InsertTextTxn.cpp +++ b/mozilla/editor/libeditor/base/InsertTextTxn.cpp @@ -20,18 +20,22 @@ #include "editor.h" #include "nsIDOMCharacterData.h" -static NS_DEFINE_IID(kInsertTextTxnIID, INSERTTEXTTXN_IID); +static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID); -// note that aEditor is not refcounted -InsertTextTxn::InsertTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, + +InsertTextTxn::InsertTextTxn() + : EditTxn() +{ +} + +nsresult InsertTextTxn::Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, const nsString& aStringToInsert) - : EditTxn(aEditor) { mElement = aElement; mOffset = aOffset; mStringToInsert = aStringToInsert; + return NS_OK; } nsresult InsertTextTxn::Do(void) @@ -60,11 +64,11 @@ nsresult InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) if ((nsnull!=aDidMerge) && (nsnull!=aTransaction)) { // if aTransaction isa InsertTextTxn, absorb it - nsCOMPtr otherTxn; - nsresult result = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); + nsCOMPtr otherTxn = aTransaction; + nsresult result=NS_OK;// = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); if (NS_SUCCEEDED(result) && (otherTxn)) { - nsString otherData; + nsAutoString otherData; otherTxn->GetData(otherData); mStringToInsert += otherData; } diff --git a/mozilla/editor/libeditor/base/InsertTextTxn.h b/mozilla/editor/libeditor/base/InsertTextTxn.h index 1e7288bb88d..134cddb790b 100644 --- a/mozilla/editor/libeditor/base/InsertTextTxn.h +++ b/mozilla/editor/libeditor/base/InsertTextTxn.h @@ -20,14 +20,14 @@ #define InsertTextTxn_h__ #include "EditTxn.h" +#include "nsIDOMCharacterData.h" +#include "nsCOMPtr.h" -#define INSERTTEXTTXN_IID \ +#define INSERT_TEXT_TXN_IID \ {/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \ 0x93276f00, 0xab2c, 0x11d2, \ {0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} } -class nsIDOMCharacterData; - /** * A transaction that changes an attribute of a content node. * This transaction covers add, remove, and change attribute. @@ -36,15 +36,13 @@ class InsertTextTxn : public EditTxn { public: - InsertTextTxn(nsEditor *aEditor, - nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert); + virtual nsresult Init(nsIDOMCharacterData *aElement, + PRUint32 aOffset, + const nsString& aStringToInsert); private: - // default ctor so that nsCOMPtr is happy - InsertTextTxn() : EditTxn(nsnull) {} + InsertTextTxn(); public: @@ -67,7 +65,7 @@ public: // override QueryInterface to handle InsertTextTxn request NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); - static const nsIID& IID() { static nsIID iid = INSERTTEXTTXN_IID; return iid; } + static const nsIID& IID() { static nsIID iid = INSERT_TEXT_TXN_IID; return iid; } virtual nsresult GetData(nsString& aResult); @@ -75,7 +73,7 @@ public: protected: /** the text element to operate upon */ - nsIDOMCharacterData *mElement; + nsCOMPtr mElement; /** the offset into mElement where the insertion is to take place */ PRUint32 mOffset; @@ -86,6 +84,8 @@ protected: /** the text to insert into mElement at mOffset */ nsString mStringToInsert; + friend class TransactionFactory; + }; #endif diff --git a/mozilla/editor/libeditor/base/JoinElementTxn.cpp b/mozilla/editor/libeditor/base/JoinElementTxn.cpp new file mode 100644 index 00000000000..e6331ffddf9 --- /dev/null +++ b/mozilla/editor/libeditor/base/JoinElementTxn.cpp @@ -0,0 +1,122 @@ +/* -*- 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 "JoinElementTxn.h" +#include "nsIDOMNodeList.h" +#include "editor.h" + + +JoinElementTxn::JoinElementTxn() + : EditTxn() +{ +} + +nsresult JoinElementTxn::Init(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode) +{ + mLeftNode = aLeftNode; + mRightNode = aRightNode; + mOffset=0; + return NS_OK; +} + +JoinElementTxn::~JoinElementTxn() +{ +} + +nsresult JoinElementTxn::Do(void) +{ + nsresult result; + + if ((mLeftNode) && (mRightNode)) + { // get the parent node + nsCOMPtrleftParent; + result = mLeftNode->GetParentNode(getter_AddRefs(leftParent)); + if ((NS_SUCCEEDED(result)) && (leftParent)) + { // verify that mLeftNode and mRightNode have the same parent + nsCOMPtrrightParent; + result = mRightNode->GetParentNode(getter_AddRefs(rightParent)); + if ((NS_SUCCEEDED(result)) && (rightParent)) + { + if (leftParent==rightParent) + { + mParent=leftParent; // set this instance mParent. + // Other methods see a non-null mParent and know all is well + nsCOMPtr childNodes; + result = mLeftNode->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(result)) && (childNodes)) + { + childNodes->GetLength(&mOffset); + } + result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE); + } + } + } + } + return result; +} + + +nsresult JoinElementTxn::Undo(void) +{ + nsresult result = nsEditor::SplitNode(mRightNode, mOffset, mLeftNode, mParent); + return result; +} + +nsresult JoinElementTxn::Redo(void) +{ + nsresult result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE); + return result; +} + +nsresult JoinElementTxn::GetIsTransient(PRBool *aIsTransient) +{ + if (nsnull!=aIsTransient) + *aIsTransient = PR_FALSE; + return NS_OK; +} + +nsresult JoinElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) +{ + if (nsnull!=aDidMerge) + *aDidMerge=PR_FALSE; + return NS_OK; +} + +nsresult JoinElementTxn::Write(nsIOutputStream *aOutputStream) +{ + return NS_OK; +} + +nsresult JoinElementTxn::GetUndoString(nsString **aString) +{ + if (nsnull!=aString) + { + **aString="Join Element"; + } + return NS_OK; +} + +nsresult JoinElementTxn::GetRedoString(nsString **aString) +{ + if (nsnull!=aString) + { + **aString="Split Element"; + } + return NS_OK; +} diff --git a/mozilla/editor/libeditor/base/JoinElementTxn.h b/mozilla/editor/libeditor/base/JoinElementTxn.h new file mode 100644 index 00000000000..d094efe5f09 --- /dev/null +++ b/mozilla/editor/libeditor/base/JoinElementTxn.h @@ -0,0 +1,86 @@ +/* -*- 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 JoinElementTxn_h__ +#define JoinElementTxn_h__ + +#include "EditTxn.h" +#include "nsIDOMNode.h" +#include "nsCOMPtr.h" + +#define JOIN_ELEMENT_TXN_IID \ +{/* 9bc5f9f0-ac48-11d2-86d8-000064657374 */ \ +0x9bc5f9f0, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } + + +/** + * A transaction that joins two elements E1 and E2 into a single node E. + * The children of E are the children of E1 followed by the children of E2. + */ +class JoinElementTxn : public EditTxn +{ +public: + + virtual nsresult Init(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode); +protected: + JoinElementTxn(); + +public: + + virtual ~JoinElementTxn(); + + virtual nsresult Do(void); + + virtual nsresult Undo(void); + + virtual nsresult Redo(void); + + virtual nsresult GetIsTransient(PRBool *aIsTransient); + + virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction); + + virtual nsresult Write(nsIOutputStream *aOutputStream); + + virtual nsresult GetUndoString(nsString **aString); + + virtual nsresult GetRedoString(nsString **aString); + +protected: + + /** the elements to operate upon. + * After the merge, mRightNode remains and mLeftNode is removed from the content tree. + */ + nsCOMPtr mLeftNode; + nsCOMPtr mRightNode; + + /** the offset into mNode where the children of mElement are split (for undo).
+ * mOffset is the index of the last child in the left node. + * -1 means the left node gets no children. + */ + PRUint32 mOffset; + + /** the parent node containing mLeftNode and mRightNode */ + nsCOMPtr mParent; + + friend class TransactionFactory; + +}; + +#endif diff --git a/mozilla/editor/libeditor/base/SplitElementTxn.cpp b/mozilla/editor/libeditor/base/SplitElementTxn.cpp index 2134cc5375c..dcabdec1865 100644 --- a/mozilla/editor/libeditor/base/SplitElementTxn.cpp +++ b/mozilla/editor/libeditor/base/SplitElementTxn.cpp @@ -17,44 +17,42 @@ */ #include "SplitElementTxn.h" -#include "editor.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" +#include "editor.h" // note that aEditor is not refcounted -SplitElementTxn::SplitElementTxn(nsEditor *aEditor, - nsIDOMNode *aNode, - PRInt32 aOffset) - : EditTxn(aEditor) +SplitElementTxn::SplitElementTxn() + : EditTxn() { - mNode = aNode; - NS_ADDREF(mNode); +} + +nsresult SplitElementTxn::Init(nsIDOMNode *aNode, + PRInt32 aOffset) +{ + mExistingRightNode = aNode; mOffset = aOffset; - mNewNode = nsnull; - mParent = nsnull; + return NS_OK; } SplitElementTxn::~SplitElementTxn() { - NS_IF_RELEASE(mNode); - NS_IF_RELEASE(mNewNode); - NS_IF_RELEASE(mParent); } nsresult SplitElementTxn::Do(void) { // create a new node - nsresult result = mNode->CloneNode(PR_FALSE, &mNewNode); - NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); + nsresult result = mExistingRightNode->CloneNode(PR_FALSE, getter_AddRefs(mNewLeftNode)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element."); - if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) + if ((NS_SUCCEEDED(result)) && (mNewLeftNode)) { // get the parent node - result = mNode->GetParentNode(&mParent); + result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent)); // insert the new node - if ((NS_SUCCEEDED(result)) && (nsnull!=mParent)) + if ((NS_SUCCEEDED(result)) && (mParent)) { - result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); + result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent); } } return result; @@ -63,13 +61,13 @@ nsresult SplitElementTxn::Do(void) nsresult SplitElementTxn::Undo(void) { // this assumes Do inserted the new node in front of the prior existing node - nsresult result = mEditor->JoinNodes(mNode, mNewNode, mParent, PR_FALSE); + nsresult result = nsEditor::JoinNodes(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); return result; } nsresult SplitElementTxn::Redo(void) { - nsresult result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); + nsresult result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent); return result; } diff --git a/mozilla/editor/libeditor/base/SplitElementTxn.h b/mozilla/editor/libeditor/base/SplitElementTxn.h index 8fb19df1fb6..051021e6bb8 100644 --- a/mozilla/editor/libeditor/base/SplitElementTxn.h +++ b/mozilla/editor/libeditor/base/SplitElementTxn.h @@ -20,10 +20,13 @@ #define SplitElementTxn_h__ #include "EditTxn.h" +#include "nsIDOMNode.h" +#include "nsCOMPtr.h" -class nsIDOMNode; -class nsIDOMElement; -class nsIDOMDocument; +#define SPLIT_ELEMENT_TXN_IID \ +{/* 690c6290-ac48-11d2-86d8-000064657374 */ \ +0x690c6290, 0xac48, 0x11d2, \ +{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } /** * A transaction that splits an element E into two identical nodes, E1 and E2 @@ -33,10 +36,12 @@ class SplitElementTxn : public EditTxn { public: - SplitElementTxn(nsEditor *aEditor, - nsIDOMNode *aNode, - PRInt32 aOffset); + virtual nsresult Init (nsIDOMNode *aNode, + PRInt32 aOffset); +protected: + SplitElementTxn(); +public: virtual ~SplitElementTxn(); virtual nsresult Do(void); @@ -58,7 +63,7 @@ public: protected: /** the element to operate upon */ - nsIDOMNode *mNode; + nsCOMPtr mExistingRightNode; /** the offset into mElement where the children of mElement are split.
* mOffset is the index of the last child in the left node. @@ -67,8 +72,12 @@ protected: PRInt32 mOffset; /** the element we create when splitting mElement */ - nsIDOMNode *mNewNode; - nsIDOMNode *mParent; + nsCOMPtr mNewLeftNode; + + /** the parent shared by mExistingRightNode and mNewLeftNode */ + nsCOMPtr mParent; + + friend class TransactionFactory; }; diff --git a/mozilla/editor/libeditor/base/TransactionFactory.cpp b/mozilla/editor/libeditor/base/TransactionFactory.cpp new file mode 100644 index 00000000000..cc4be1af14f --- /dev/null +++ b/mozilla/editor/libeditor/base/TransactionFactory.cpp @@ -0,0 +1,75 @@ +/* -*- 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 "TransactionFactory.h" +// transactions this factory knows how to build +#include "InsertTextTxn.h" +#include "DeleteTextTxn.h" +#include "CreateElementTxn.h" +#include "DeleteElementTxn.h" +#include "DeleteRangeTxn.h" +#include "ChangeAttributeTxn.h" +#include "SplitElementTxn.h" +#include "JoinElementTxn.h" + +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); +static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID); +static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID); +static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID); +static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID); + +TransactionFactory::TransactionFactory() +{ +} + +TransactionFactory::~TransactionFactory() +{ +} + +nsresult +TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult) +{ + nsresult result = NS_OK; + *aResult = nsnull; + if (aTxnType.Equals(kInsertTextTxnIID)) + *aResult = new InsertTextTxn(); + else if (aTxnType.Equals(kDeleteTextTxnIID)) + *aResult = new DeleteTextTxn(); + else if (aTxnType.Equals(kCreateElementTxnIID)) + *aResult = new CreateElementTxn(); + else if (aTxnType.Equals(kDeleteElementTxnIID)) + *aResult = new DeleteElementTxn(); + else if (aTxnType.Equals(kDeleteRangeTxnIID)) + *aResult = new DeleteRangeTxn(); + else if (aTxnType.Equals(kChangeAttributeTxnIID)) + *aResult = new ChangeAttributeTxn(); + else if (aTxnType.Equals(kSplitElementTxnIID)) + *aResult = new SplitElementTxn(); + else if (aTxnType.Equals(kJoinElementTxnIID)) + *aResult = new JoinElementTxn(); + + if (nsnull==*aResult) + result = NS_ERROR_INVALID_ARG; + + return result; +} + + diff --git a/mozilla/editor/libeditor/base/TransactionFactory.h b/mozilla/editor/libeditor/base/TransactionFactory.h new file mode 100644 index 00000000000..58276d5679f --- /dev/null +++ b/mozilla/editor/libeditor/base/TransactionFactory.h @@ -0,0 +1,41 @@ +/* -*- 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 TransactionFactory_h__ +#define TransactionFactory_h__ + +#include "nsISupports.h" + +class EditTxn; + +/** + * This class instantiates and optionally recycles edit transactions + * A recycler would be a separate static object, since this class does not get instantiated + */ +class TransactionFactory +{ +protected: + TransactionFactory(); + virtual ~TransactionFactory(); + +public: + static nsresult GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult); + +}; + +#endif