From b259f714c6952f7384d8bea9236a946ba29704af Mon Sep 17 00:00:00 2001 From: "buster%netscape.com" Date: Mon, 22 Feb 1999 15:53:31 +0000 Subject: [PATCH] caught up to new ScrollIntoView changes. most transactions now properly set selection after modifying content, for Do, Undo, Redo. lots of cleanup and minor bug fixes. --- editor/base/CreateElementTxn.cpp | 94 +++++++++++++++++----- editor/base/CreateElementTxn.h | 8 +- editor/base/DeleteRangeTxn.cpp | 9 ++- editor/base/DeleteTextTxn.cpp | 50 ++++++++++-- editor/base/DeleteTextTxn.h | 10 ++- editor/base/EditAggregateTxn.cpp | 8 +- editor/base/InsertTextTxn.cpp | 25 +++--- editor/base/nsEditor.cpp | 38 ++++++++- editor/base/nsIEditorSupport.h | 2 + editor/base/nsTextEditor.cpp | 49 +---------- editor/base/nsTextEditor.h | 4 - editor/libeditor/base/CreateElementTxn.cpp | 94 +++++++++++++++++----- editor/libeditor/base/CreateElementTxn.h | 8 +- editor/libeditor/base/DeleteRangeTxn.cpp | 9 ++- editor/libeditor/base/DeleteTextTxn.cpp | 50 ++++++++++-- editor/libeditor/base/DeleteTextTxn.h | 10 ++- editor/libeditor/base/EditAggregateTxn.cpp | 8 +- editor/libeditor/base/InsertTextTxn.cpp | 25 +++--- editor/libeditor/base/nsEditor.cpp | 38 ++++++++- editor/libeditor/base/nsIEditorSupport.h | 2 + 20 files changed, 393 insertions(+), 148 deletions(-) diff --git a/editor/base/CreateElementTxn.cpp b/editor/base/CreateElementTxn.cpp index 3fcb1bfc673..2c81569033e 100644 --- a/editor/base/CreateElementTxn.cpp +++ b/editor/base/CreateElementTxn.cpp @@ -17,21 +17,25 @@ */ #include "CreateElementTxn.h" +#include "nsIDOMDocument.h" #include "nsIDOMNodeList.h" +#include "nsIDOMSelection.h" +#include "nsIEditorSupport.h" CreateElementTxn::CreateElementTxn() : EditTxn() { } -nsresult CreateElementTxn::Init(nsIDOMDocument *aDoc, +nsresult CreateElementTxn::Init(nsIEditor *aEditor, const nsString& aTag, nsIDOMNode *aParent, PRUint32 aOffsetInParent) { - if ((nsnull!=aDoc) && (nsnull!=aParent)) + NS_ASSERTION(aEditor&&aParent, "null args"); + if (aEditor && aParent) { - mDoc = do_QueryInterface(aDoc); + mEditor = do_QueryInterface(aEditor); mTag = aTag; mParent = do_QueryInterface(aParent); mOffsetInParent = aOffsetInParent; @@ -57,28 +61,50 @@ CreateElementTxn::~CreateElementTxn() nsresult CreateElementTxn::Do(void) { - // create a new node - nsresult result = mDoc->CreateElement(mTag, getter_AddRefs(mNewNode)); - NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); - - if ((NS_SUCCEEDED(result)) && (mNewNode)) + NS_ASSERTION(mEditor, "bad state -- null editor"); + nsresult result = NS_ERROR_NULL_POINTER; + if (mEditor) { - // insert the new node - nsCOMPtr resultNode; - if (CreateElementTxn::eAppend==mOffsetInParent) + // create a new node + nsCOMPtrdoc; + result = mEditor->GetDocument(getter_AddRefs(doc)); + if ((NS_SUCCEEDED(result)) && (doc)) { - result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode)); - } - else - { - nsCOMPtr childNodes; - result = mParent->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(result)) && (childNodes)) + result = doc->CreateElement(mTag, getter_AddRefs(mNewNode)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); + if ((NS_SUCCEEDED(result)) && (mNewNode)) { - result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); - if ((NS_SUCCEEDED(result)) && (mRefNode)) + // insert the new node + nsCOMPtr resultNode; + if (CreateElementTxn::eAppend==mOffsetInParent) { - result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode)); + } + else + { + nsCOMPtr childNodes; + result = mParent->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(result)) && (childNodes)) + { + result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); + if ((NS_SUCCEEDED(result)) && (mRefNode)) + { + result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + nsresult selectionResult = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + PRInt32 offset=0; + nsIEditorSupport::GetChildOffset(mNewNode, mParent, offset); + selectionResult = selection->Collapse(mParent, offset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } + } + } } } } @@ -90,6 +116,19 @@ nsresult CreateElementTxn::Undo(void) { nsCOMPtr resultNode; nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + nsresult selectionResult = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + PRInt32 offset=0; + nsIEditorSupport::GetChildOffset(mRefNode, mParent, offset); + selectionResult = selection->Collapse(mParent, offset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } return result; } @@ -97,6 +136,19 @@ nsresult CreateElementTxn::Redo(void) { nsCOMPtr resultNode; nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) { + selection->StartBatchChanges(); + PRInt32 offset=0; + nsIEditorSupport::GetChildOffset(mNewNode, mParent, offset); + nsresult selectionResult = selection->Collapse(mParent, offset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } return result; } diff --git a/editor/base/CreateElementTxn.h b/editor/base/CreateElementTxn.h index 6cbdd9dc4b7..35f59ad4377 100644 --- a/editor/base/CreateElementTxn.h +++ b/editor/base/CreateElementTxn.h @@ -20,7 +20,7 @@ #define CreateElementTxn_h__ #include "EditTxn.h" -#include "nsIDOMDocument.h" +#include "nsIEditor.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" #include "nsCOMPtr.h" @@ -40,13 +40,13 @@ public: enum { eAppend=-1 }; /** Initialize the transaction. - * @param aDoc the document containing aParent + * @param aEditor the provider of basic editing functionality * @param aTag the tag (P, HR, TABLE, etc.) for the new element * @param aParent the node into which the new element will be inserted * @param aOffsetInParent the location in aParent to insert the new element * if eAppend, the new element is appended as the last child */ - virtual nsresult Init(nsIDOMDocument *aDoc, + virtual nsresult Init(nsIEditor *aEditor, const nsString& aTag, nsIDOMNode *aParent, PRUint32 aOffsetInParent); @@ -75,7 +75,7 @@ public: protected: /** the document into which the new node will be inserted */ - nsCOMPtr mDoc; + nsCOMPtr mEditor; /** the tag (mapping to object type) for the new element */ nsString mTag; diff --git a/editor/base/DeleteRangeTxn.cpp b/editor/base/DeleteRangeTxn.cpp index e5a9d3c0d04..732748c5a1d 100644 --- a/editor/base/DeleteRangeTxn.cpp +++ b/editor/base/DeleteRangeTxn.cpp @@ -231,7 +231,12 @@ nsresult DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); if (nsnull!=txn) { - txn->Init(textNode, aStartOffset, (aEndOffset-aStartOffset)+1); + PRInt32 numToDel; + if (aStartOffset==aEndOffset) + numToDel = 1; + else + numToDel = aEndOffset-aStartOffset; + txn->Init(mEditor, textNode, aStartOffset, numToDel); AppendChild(txn); } } @@ -293,7 +298,7 @@ nsresult DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); if (nsnull!=txn) { - txn->Init(textNode, start, numToDelete); + txn->Init(mEditor, textNode, start, numToDelete); AppendChild(txn); } else diff --git a/editor/base/DeleteTextTxn.cpp b/editor/base/DeleteTextTxn.cpp index cb8d91cc6ec..f66372c2a81 100644 --- a/editor/base/DeleteTextTxn.cpp +++ b/editor/base/DeleteTextTxn.cpp @@ -18,6 +18,7 @@ #include "DeleteTextTxn.h" #include "nsIDOMCharacterData.h" +#include "nsIDOMSelection.h" DeleteTextTxn::DeleteTextTxn() @@ -25,10 +26,13 @@ DeleteTextTxn::DeleteTextTxn() { } -nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement, +nsresult DeleteTextTxn::Init(nsIEditor *aEditor, + nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aNumCharsToDelete) { + NS_ASSERTION(aEditor&&aElement, "bad arg"); + mEditor = do_QueryInterface(aEditor); mElement = do_QueryInterface(aElement); mOffset = aOffset; mNumCharsToDelete = aNumCharsToDelete; @@ -38,15 +42,49 @@ nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement, nsresult DeleteTextTxn::Do(void) { - // get the text that we're about to delete - nsresult result = mElement->SubstringData(mOffset, mNumCharsToDelete, mDeletedText); - NS_ASSERTION(NS_SUCCEEDED(result), "could not get text to delete."); - return (mElement->DeleteData(mOffset, mNumCharsToDelete)); + nsresult result = NS_ERROR_NULL_POINTER; + if (mEditor && mElement) + { + // get the text that we're about to delete + result = mElement->SubstringData(mOffset, mNumCharsToDelete, mDeletedText); + NS_ASSERTION(NS_SUCCEEDED(result), "could not get text to delete."); + result = mElement->DeleteData(mOffset, mNumCharsToDelete); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + nsresult selectionResult = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + selectionResult = selection->Collapse(mElement, mOffset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } + } + return result; } +//XXX: we may want to store the selection state and restore it properly +// was it an insertion point or an extended selection? nsresult DeleteTextTxn::Undo(void) { - return (mElement->InsertData(mOffset, mDeletedText)); + nsresult result = NS_ERROR_NULL_POINTER; + if (mEditor && mElement) + { + result = mElement->InsertData(mOffset, mDeletedText); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + nsresult selectionResult = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + selectionResult = selection->Collapse(mElement, mOffset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } + } + return result; } nsresult DeleteTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) diff --git a/editor/base/DeleteTextTxn.h b/editor/base/DeleteTextTxn.h index 60bc9faf6b4..17d344248f9 100644 --- a/editor/base/DeleteTextTxn.h +++ b/editor/base/DeleteTextTxn.h @@ -20,6 +20,7 @@ #define DeleteTextTxn_h__ #include "EditTxn.h" +#include "nsIEditor.h" #include "nsIDOMCharacterData.h" #include "nsCOMPtr.h" @@ -36,11 +37,13 @@ class DeleteTextTxn : public EditTxn public: /** initialize the transaction. + * @param aEditor the provider of basic editing operations * @param aElement the content node to remove text from * @param aOffset the location in aElement to begin the deletion * @param aNumCharsToDelete the number of characters to delete. Not the number of bytes! */ - virtual nsresult Init(nsIDOMCharacterData *aElement, + virtual nsresult Init(nsIEditor *aEditor, + nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aNumCharsToDelete); @@ -62,7 +65,10 @@ public: virtual nsresult GetRedoString(nsString **aString); protected: - + + /** the provider of basic editing operations */ + nsCOMPtr mEditor; + /** the text element to operate upon */ nsCOMPtr mElement; diff --git a/editor/base/EditAggregateTxn.cpp b/editor/base/EditAggregateTxn.cpp index 78881db9134..d84031c9c8b 100644 --- a/editor/base/EditAggregateTxn.cpp +++ b/editor/base/EditAggregateTxn.cpp @@ -113,8 +113,12 @@ nsresult EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction if (nsnull!=mChildren) { PRInt32 count = mChildren->Count(); - EditTxn *txn = (EditTxn*)(mChildren->ElementAt(count-1)); - result = txn->Merge(aDidMerge, aTransaction); + NS_ASSERTION(count>0, "bad count"); + if (0ElementAt(count-1)); + result = txn->Merge(aDidMerge, aTransaction); + } } return result; diff --git a/editor/base/InsertTextTxn.cpp b/editor/base/InsertTextTxn.cpp index 4b595534444..92bcab46aee 100644 --- a/editor/base/InsertTextTxn.cpp +++ b/editor/base/InsertTextTxn.cpp @@ -56,16 +56,18 @@ nsresult InsertTextTxn::Do(void) { // advance caret: This requires the presentation shell to get the selection. nsCOMPtr selection; - nsresult res = mPresShell->GetSelection(getter_AddRefs(selection)); - selection->StartBatchChanges(); - res = mElement->InsertData(mOffset, mStringToInsert); - if (NS_SUCCEEDED(res)) { - res = selection->Collapse(mElement, mOffset+mStringToInsert.Length()); + nsresult result = mPresShell->GetSelection(getter_AddRefs(selection)); + NS_ASSERTION(selection,"Could not get selection in InsertTextTxn::Do\n"); + if (NS_SUCCEEDED(result) && selection) { + selection->StartBatchChanges(); + result = mElement->InsertData(mOffset, mStringToInsert); + if (NS_SUCCEEDED(result)) { + result = selection->Collapse(mElement, mOffset+mStringToInsert.Length()); + NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); + } + selection->EndBatchChanges(); } - else - NS_ASSERTION(PR_FALSE,"Could not get selection in InsertTextTxn::Do\n"); - selection->EndBatchChanges(); - return res; + return result; } nsresult InsertTextTxn::Undo(void) @@ -77,8 +79,11 @@ nsresult InsertTextTxn::Undo(void) { // set the selection to the insertion point where the string was removed nsCOMPtr selection; result = mPresShell->GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(result)) { + if (NS_SUCCEEDED(result) && selection) { + selection->StartBatchChanges(); result = selection->Collapse(mElement, mOffset); + NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); } } return result; diff --git a/editor/base/nsEditor.cpp b/editor/base/nsEditor.cpp index 420aa3be98c..fa217aa87e0 100644 --- a/editor/base/nsEditor.cpp +++ b/editor/base/nsEditor.cpp @@ -711,7 +711,7 @@ nsresult nsEditor::CreateTxnForCreateElement(const nsString& aTag, { result = TransactionFactory::GetNewTransaction(kCreateElementTxnIID, (EditTxn **)aTxn); if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(mDoc, aTag, aParent, aPosition); + result = (*aTxn)->Init(this, aTag, aParent, aPosition); } } return result; @@ -866,7 +866,7 @@ nsresult nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData *aElement, { result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)aTxn); if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(aElement, aOffset, aLength); + result = (*aTxn)->Init(this, aElement, aOffset, aLength); } } return result; @@ -1486,6 +1486,40 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, return result; } +nsresult nsIEditorSupport::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset) +{ + NS_ASSERTION((aChild && aParent), "bad args"); + nsresult result = NS_ERROR_NULL_POINTER; + if (aChild && aParent) + { + nsCOMPtr childNodes; + result = aParent->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(result)) && (childNodes)) + { + PRInt32 i=0; + for ( ; NS_SUCCEEDED(result); i++) + { + nsCOMPtr childNode; + result = childNodes->Item(i, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(result)) && (childNode)) + { + if (childNode.get()==aChild) + { + aOffset = i; + break; + } + } + else if (!childNode) + result = NS_ERROR_NULL_POINTER; + } + } + else if (!childNodes) + result = NS_ERROR_NULL_POINTER; + } + return result; +} + + //END nsEditor Private methods diff --git a/editor/base/nsIEditorSupport.h b/editor/base/nsIEditorSupport.h index c3f7aad238f..153f910f095 100644 --- a/editor/base/nsIEditorSupport.h +++ b/editor/base/nsIEditorSupport.h @@ -65,6 +65,8 @@ public: nsIDOMNode *aNodeToJoin, nsIDOMNode *aParent, PRBool aNodeToKeepIsFirst)=0; + + static nsresult GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset); diff --git a/editor/base/nsTextEditor.cpp b/editor/base/nsTextEditor.cpp index 8fb9c718fbf..571e567ab02 100644 --- a/editor/base/nsTextEditor.cpp +++ b/editor/base/nsTextEditor.cpp @@ -17,6 +17,7 @@ */ #include "nsTextEditor.h" +#include "nsIEditorSupport.h" #include "nsEditorEventListeners.h" #include "nsIDOMDocument.h" #include "nsIDOMEventReceiver.h" @@ -167,9 +168,6 @@ nsresult nsTextEditor::InsertText(const nsString& aStringToInsert) if (mEditor) { result = mEditor->InsertText(aStringToInsert); - if (NS_SUCCEEDED(result)) { - mEditor->ScrollIntoView(PR_TRUE); - } } return result; } @@ -207,7 +205,7 @@ nsresult nsTextEditor::InsertBreak(PRBool aCtrlKey) result = node->GetParentNode(getter_AddRefs(parentNode)); result = mEditor->SplitNode(node, offset); // now get the node's offset in it's parent, and insert the new BR there - result = GetChildOffset(node, parentNode, offset); + result = nsIEditorSupport::GetChildOffset(node, parentNode, offset); nsAutoString tag("BR"); result = mEditor->CreateNode(tag, parentNode, offset); selection->Collapse(parentNode, offset); @@ -232,12 +230,8 @@ nsresult nsTextEditor::EnableUndo(PRBool aEnable) nsresult nsTextEditor::Undo(PRUint32 aCount) { nsresult result=NS_ERROR_NOT_INITIALIZED; - if (mEditor) - { + if (mEditor) { result = mEditor->Undo(aCount); - if (NS_SUCCEEDED(result)) { - mEditor->ScrollIntoView(PR_TRUE); - } } return result; } @@ -258,9 +252,6 @@ nsresult nsTextEditor::Redo(PRUint32 aCount) if (mEditor) { result = mEditor->Redo(aCount); - if (NS_SUCCEEDED(result)) { - mEditor->ScrollIntoView(PR_TRUE); - } } return result; } @@ -440,37 +431,3 @@ nsTextEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) } -// utility methods - -nsresult nsTextEditor::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset) -{ - NS_ASSERTION((aChild && aParent), "bad args"); - nsresult result = NS_ERROR_NULL_POINTER; - if (aChild && aParent) - { - nsCOMPtr childNodes; - result = aParent->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(result)) && (childNodes)) - { - PRInt32 i=0; - for ( ; NS_SUCCEEDED(result); i++) - { - nsCOMPtr childNode; - result = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(result)) && (childNode)) - { - if (childNode.get()==aChild) - { - aOffset = i; - break; - } - } - else if (!childNode) - result = NS_ERROR_NULL_POINTER; - } - } - else if (!childNodes) - result = NS_ERROR_NULL_POINTER; - } - return result; -} diff --git a/editor/base/nsTextEditor.h b/editor/base/nsTextEditor.h index 7cc8bb7208b..9e7fb65980e 100644 --- a/editor/base/nsTextEditor.h +++ b/editor/base/nsTextEditor.h @@ -77,10 +77,6 @@ public: virtual nsresult OutputText(nsIOutputStream *aOutputStream); virtual nsresult OutputHTML(nsIOutputStream *aOutputStream); -// Utility methods -protected: - virtual nsresult GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset); - // Data members protected: nsCOMPtr mEditor; diff --git a/editor/libeditor/base/CreateElementTxn.cpp b/editor/libeditor/base/CreateElementTxn.cpp index 3fcb1bfc673..2c81569033e 100644 --- a/editor/libeditor/base/CreateElementTxn.cpp +++ b/editor/libeditor/base/CreateElementTxn.cpp @@ -17,21 +17,25 @@ */ #include "CreateElementTxn.h" +#include "nsIDOMDocument.h" #include "nsIDOMNodeList.h" +#include "nsIDOMSelection.h" +#include "nsIEditorSupport.h" CreateElementTxn::CreateElementTxn() : EditTxn() { } -nsresult CreateElementTxn::Init(nsIDOMDocument *aDoc, +nsresult CreateElementTxn::Init(nsIEditor *aEditor, const nsString& aTag, nsIDOMNode *aParent, PRUint32 aOffsetInParent) { - if ((nsnull!=aDoc) && (nsnull!=aParent)) + NS_ASSERTION(aEditor&&aParent, "null args"); + if (aEditor && aParent) { - mDoc = do_QueryInterface(aDoc); + mEditor = do_QueryInterface(aEditor); mTag = aTag; mParent = do_QueryInterface(aParent); mOffsetInParent = aOffsetInParent; @@ -57,28 +61,50 @@ CreateElementTxn::~CreateElementTxn() nsresult CreateElementTxn::Do(void) { - // create a new node - nsresult result = mDoc->CreateElement(mTag, getter_AddRefs(mNewNode)); - NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); - - if ((NS_SUCCEEDED(result)) && (mNewNode)) + NS_ASSERTION(mEditor, "bad state -- null editor"); + nsresult result = NS_ERROR_NULL_POINTER; + if (mEditor) { - // insert the new node - nsCOMPtr resultNode; - if (CreateElementTxn::eAppend==mOffsetInParent) + // create a new node + nsCOMPtrdoc; + result = mEditor->GetDocument(getter_AddRefs(doc)); + if ((NS_SUCCEEDED(result)) && (doc)) { - result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode)); - } - else - { - nsCOMPtr childNodes; - result = mParent->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(result)) && (childNodes)) + result = doc->CreateElement(mTag, getter_AddRefs(mNewNode)); + NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); + if ((NS_SUCCEEDED(result)) && (mNewNode)) { - result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); - if ((NS_SUCCEEDED(result)) && (mRefNode)) + // insert the new node + nsCOMPtr resultNode; + if (CreateElementTxn::eAppend==mOffsetInParent) { - result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode)); + } + else + { + nsCOMPtr childNodes; + result = mParent->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(result)) && (childNodes)) + { + result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); + if ((NS_SUCCEEDED(result)) && (mRefNode)) + { + result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + nsresult selectionResult = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + PRInt32 offset=0; + nsIEditorSupport::GetChildOffset(mNewNode, mParent, offset); + selectionResult = selection->Collapse(mParent, offset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } + } + } } } } @@ -90,6 +116,19 @@ nsresult CreateElementTxn::Undo(void) { nsCOMPtr resultNode; nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + nsresult selectionResult = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + PRInt32 offset=0; + nsIEditorSupport::GetChildOffset(mRefNode, mParent, offset); + selectionResult = selection->Collapse(mParent, offset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } return result; } @@ -97,6 +136,19 @@ nsresult CreateElementTxn::Redo(void) { nsCOMPtr resultNode; nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) { + selection->StartBatchChanges(); + PRInt32 offset=0; + nsIEditorSupport::GetChildOffset(mNewNode, mParent, offset); + nsresult selectionResult = selection->Collapse(mParent, offset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } return result; } diff --git a/editor/libeditor/base/CreateElementTxn.h b/editor/libeditor/base/CreateElementTxn.h index 6cbdd9dc4b7..35f59ad4377 100644 --- a/editor/libeditor/base/CreateElementTxn.h +++ b/editor/libeditor/base/CreateElementTxn.h @@ -20,7 +20,7 @@ #define CreateElementTxn_h__ #include "EditTxn.h" -#include "nsIDOMDocument.h" +#include "nsIEditor.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" #include "nsCOMPtr.h" @@ -40,13 +40,13 @@ public: enum { eAppend=-1 }; /** Initialize the transaction. - * @param aDoc the document containing aParent + * @param aEditor the provider of basic editing functionality * @param aTag the tag (P, HR, TABLE, etc.) for the new element * @param aParent the node into which the new element will be inserted * @param aOffsetInParent the location in aParent to insert the new element * if eAppend, the new element is appended as the last child */ - virtual nsresult Init(nsIDOMDocument *aDoc, + virtual nsresult Init(nsIEditor *aEditor, const nsString& aTag, nsIDOMNode *aParent, PRUint32 aOffsetInParent); @@ -75,7 +75,7 @@ public: protected: /** the document into which the new node will be inserted */ - nsCOMPtr mDoc; + nsCOMPtr mEditor; /** the tag (mapping to object type) for the new element */ nsString mTag; diff --git a/editor/libeditor/base/DeleteRangeTxn.cpp b/editor/libeditor/base/DeleteRangeTxn.cpp index e5a9d3c0d04..732748c5a1d 100644 --- a/editor/libeditor/base/DeleteRangeTxn.cpp +++ b/editor/libeditor/base/DeleteRangeTxn.cpp @@ -231,7 +231,12 @@ nsresult DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); if (nsnull!=txn) { - txn->Init(textNode, aStartOffset, (aEndOffset-aStartOffset)+1); + PRInt32 numToDel; + if (aStartOffset==aEndOffset) + numToDel = 1; + else + numToDel = aEndOffset-aStartOffset; + txn->Init(mEditor, textNode, aStartOffset, numToDel); AppendChild(txn); } } @@ -293,7 +298,7 @@ nsresult DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn); if (nsnull!=txn) { - txn->Init(textNode, start, numToDelete); + txn->Init(mEditor, textNode, start, numToDelete); AppendChild(txn); } else diff --git a/editor/libeditor/base/DeleteTextTxn.cpp b/editor/libeditor/base/DeleteTextTxn.cpp index cb8d91cc6ec..f66372c2a81 100644 --- a/editor/libeditor/base/DeleteTextTxn.cpp +++ b/editor/libeditor/base/DeleteTextTxn.cpp @@ -18,6 +18,7 @@ #include "DeleteTextTxn.h" #include "nsIDOMCharacterData.h" +#include "nsIDOMSelection.h" DeleteTextTxn::DeleteTextTxn() @@ -25,10 +26,13 @@ DeleteTextTxn::DeleteTextTxn() { } -nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement, +nsresult DeleteTextTxn::Init(nsIEditor *aEditor, + nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aNumCharsToDelete) { + NS_ASSERTION(aEditor&&aElement, "bad arg"); + mEditor = do_QueryInterface(aEditor); mElement = do_QueryInterface(aElement); mOffset = aOffset; mNumCharsToDelete = aNumCharsToDelete; @@ -38,15 +42,49 @@ nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement, nsresult DeleteTextTxn::Do(void) { - // get the text that we're about to delete - nsresult result = mElement->SubstringData(mOffset, mNumCharsToDelete, mDeletedText); - NS_ASSERTION(NS_SUCCEEDED(result), "could not get text to delete."); - return (mElement->DeleteData(mOffset, mNumCharsToDelete)); + nsresult result = NS_ERROR_NULL_POINTER; + if (mEditor && mElement) + { + // get the text that we're about to delete + result = mElement->SubstringData(mOffset, mNumCharsToDelete, mDeletedText); + NS_ASSERTION(NS_SUCCEEDED(result), "could not get text to delete."); + result = mElement->DeleteData(mOffset, mNumCharsToDelete); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + nsresult selectionResult = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + selectionResult = selection->Collapse(mElement, mOffset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } + } + return result; } +//XXX: we may want to store the selection state and restore it properly +// was it an insertion point or an extended selection? nsresult DeleteTextTxn::Undo(void) { - return (mElement->InsertData(mOffset, mDeletedText)); + nsresult result = NS_ERROR_NULL_POINTER; + if (mEditor && mElement) + { + result = mElement->InsertData(mOffset, mDeletedText); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr selection; + nsresult selectionResult = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_SUCCEEDED(selectionResult) && selection) { + selection->StartBatchChanges(); + selectionResult = selection->Collapse(mElement, mOffset); + NS_ASSERTION((NS_SUCCEEDED(selectionResult)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); + } + } + } + return result; } nsresult DeleteTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) diff --git a/editor/libeditor/base/DeleteTextTxn.h b/editor/libeditor/base/DeleteTextTxn.h index 60bc9faf6b4..17d344248f9 100644 --- a/editor/libeditor/base/DeleteTextTxn.h +++ b/editor/libeditor/base/DeleteTextTxn.h @@ -20,6 +20,7 @@ #define DeleteTextTxn_h__ #include "EditTxn.h" +#include "nsIEditor.h" #include "nsIDOMCharacterData.h" #include "nsCOMPtr.h" @@ -36,11 +37,13 @@ class DeleteTextTxn : public EditTxn public: /** initialize the transaction. + * @param aEditor the provider of basic editing operations * @param aElement the content node to remove text from * @param aOffset the location in aElement to begin the deletion * @param aNumCharsToDelete the number of characters to delete. Not the number of bytes! */ - virtual nsresult Init(nsIDOMCharacterData *aElement, + virtual nsresult Init(nsIEditor *aEditor, + nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aNumCharsToDelete); @@ -62,7 +65,10 @@ public: virtual nsresult GetRedoString(nsString **aString); protected: - + + /** the provider of basic editing operations */ + nsCOMPtr mEditor; + /** the text element to operate upon */ nsCOMPtr mElement; diff --git a/editor/libeditor/base/EditAggregateTxn.cpp b/editor/libeditor/base/EditAggregateTxn.cpp index 78881db9134..d84031c9c8b 100644 --- a/editor/libeditor/base/EditAggregateTxn.cpp +++ b/editor/libeditor/base/EditAggregateTxn.cpp @@ -113,8 +113,12 @@ nsresult EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction if (nsnull!=mChildren) { PRInt32 count = mChildren->Count(); - EditTxn *txn = (EditTxn*)(mChildren->ElementAt(count-1)); - result = txn->Merge(aDidMerge, aTransaction); + NS_ASSERTION(count>0, "bad count"); + if (0ElementAt(count-1)); + result = txn->Merge(aDidMerge, aTransaction); + } } return result; diff --git a/editor/libeditor/base/InsertTextTxn.cpp b/editor/libeditor/base/InsertTextTxn.cpp index 4b595534444..92bcab46aee 100644 --- a/editor/libeditor/base/InsertTextTxn.cpp +++ b/editor/libeditor/base/InsertTextTxn.cpp @@ -56,16 +56,18 @@ nsresult InsertTextTxn::Do(void) { // advance caret: This requires the presentation shell to get the selection. nsCOMPtr selection; - nsresult res = mPresShell->GetSelection(getter_AddRefs(selection)); - selection->StartBatchChanges(); - res = mElement->InsertData(mOffset, mStringToInsert); - if (NS_SUCCEEDED(res)) { - res = selection->Collapse(mElement, mOffset+mStringToInsert.Length()); + nsresult result = mPresShell->GetSelection(getter_AddRefs(selection)); + NS_ASSERTION(selection,"Could not get selection in InsertTextTxn::Do\n"); + if (NS_SUCCEEDED(result) && selection) { + selection->StartBatchChanges(); + result = mElement->InsertData(mOffset, mStringToInsert); + if (NS_SUCCEEDED(result)) { + result = selection->Collapse(mElement, mOffset+mStringToInsert.Length()); + NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); + } + selection->EndBatchChanges(); } - else - NS_ASSERTION(PR_FALSE,"Could not get selection in InsertTextTxn::Do\n"); - selection->EndBatchChanges(); - return res; + return result; } nsresult InsertTextTxn::Undo(void) @@ -77,8 +79,11 @@ nsresult InsertTextTxn::Undo(void) { // set the selection to the insertion point where the string was removed nsCOMPtr selection; result = mPresShell->GetSelection(getter_AddRefs(selection)); - if (NS_SUCCEEDED(result)) { + if (NS_SUCCEEDED(result) && selection) { + selection->StartBatchChanges(); result = selection->Collapse(mElement, mOffset); + NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); + selection->EndBatchChanges(); } } return result; diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 420aa3be98c..fa217aa87e0 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -711,7 +711,7 @@ nsresult nsEditor::CreateTxnForCreateElement(const nsString& aTag, { result = TransactionFactory::GetNewTransaction(kCreateElementTxnIID, (EditTxn **)aTxn); if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(mDoc, aTag, aParent, aPosition); + result = (*aTxn)->Init(this, aTag, aParent, aPosition); } } return result; @@ -866,7 +866,7 @@ nsresult nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData *aElement, { result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)aTxn); if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(aElement, aOffset, aLength); + result = (*aTxn)->Init(this, aElement, aOffset, aLength); } } return result; @@ -1486,6 +1486,40 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, return result; } +nsresult nsIEditorSupport::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset) +{ + NS_ASSERTION((aChild && aParent), "bad args"); + nsresult result = NS_ERROR_NULL_POINTER; + if (aChild && aParent) + { + nsCOMPtr childNodes; + result = aParent->GetChildNodes(getter_AddRefs(childNodes)); + if ((NS_SUCCEEDED(result)) && (childNodes)) + { + PRInt32 i=0; + for ( ; NS_SUCCEEDED(result); i++) + { + nsCOMPtr childNode; + result = childNodes->Item(i, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(result)) && (childNode)) + { + if (childNode.get()==aChild) + { + aOffset = i; + break; + } + } + else if (!childNode) + result = NS_ERROR_NULL_POINTER; + } + } + else if (!childNodes) + result = NS_ERROR_NULL_POINTER; + } + return result; +} + + //END nsEditor Private methods diff --git a/editor/libeditor/base/nsIEditorSupport.h b/editor/libeditor/base/nsIEditorSupport.h index c3f7aad238f..153f910f095 100644 --- a/editor/libeditor/base/nsIEditorSupport.h +++ b/editor/libeditor/base/nsIEditorSupport.h @@ -65,6 +65,8 @@ public: nsIDOMNode *aNodeToJoin, nsIDOMNode *aParent, PRBool aNodeToKeepIsFirst)=0; + + static nsresult GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset);