From 11faed23b2e50330304300ae4564cf0c60caef03 Mon Sep 17 00:00:00 2001 From: "jfrancis%netscape.com" Date: Tue, 4 Jan 2000 03:09:41 +0000 Subject: [PATCH] Fix for bug 21346. This is actually a major reworking of how the low level editor txns perturb selection. There is a mode now for telling the txns not to disturb the selection. There is also a fix for unlisting lists (sometimes list items got merged unintentionally when unlisting). git-svn-id: svn://10.0.0.236/trunk@56714 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/editor/base/CreateElementTxn.cpp | 84 ++++---- mozilla/editor/base/DeleteRangeTxn.cpp | 78 ++++---- mozilla/editor/base/DeleteTextTxn.cpp | 26 +-- mozilla/editor/base/InsertElementTxn.cpp | 27 ++- mozilla/editor/base/InsertTextTxn.cpp | 50 +++-- mozilla/editor/base/InsertTextTxn.h | 8 +- mozilla/editor/base/JoinElementTxn.cpp | 28 ++- mozilla/editor/base/JoinElementTxn.h | 5 +- mozilla/editor/base/SplitElementTxn.cpp | 31 ++- mozilla/editor/base/SplitElementTxn.h | 6 +- mozilla/editor/base/nsEditor.cpp | 179 ++++++++++++++---- mozilla/editor/base/nsEditor.h | 22 ++- mozilla/editor/base/nsEditorUtils.h | 31 +++ mozilla/editor/base/nsHTMLEditRules.cpp | 67 +++++-- mozilla/editor/base/nsHTMLEditRules.h | 2 +- mozilla/editor/base/nsHTMLEditor.cpp | 6 +- mozilla/editor/base/nsTextEditRules.cpp | 3 - .../libeditor/base/CreateElementTxn.cpp | 84 ++++---- .../editor/libeditor/base/DeleteRangeTxn.cpp | 78 ++++---- .../editor/libeditor/base/DeleteTextTxn.cpp | 26 +-- .../libeditor/base/InsertElementTxn.cpp | 27 ++- .../editor/libeditor/base/InsertTextTxn.cpp | 50 +++-- mozilla/editor/libeditor/base/InsertTextTxn.h | 8 +- .../editor/libeditor/base/JoinElementTxn.cpp | 28 ++- .../editor/libeditor/base/JoinElementTxn.h | 5 +- .../editor/libeditor/base/SplitElementTxn.cpp | 31 ++- .../editor/libeditor/base/SplitElementTxn.h | 6 +- mozilla/editor/libeditor/base/nsEditor.cpp | 179 ++++++++++++++---- mozilla/editor/libeditor/base/nsEditor.h | 22 ++- mozilla/editor/libeditor/base/nsEditorUtils.h | 31 +++ .../editor/libeditor/html/nsHTMLEditRules.cpp | 67 +++++-- .../editor/libeditor/html/nsHTMLEditRules.h | 2 +- .../editor/libeditor/html/nsHTMLEditor.cpp | 6 +- .../editor/libeditor/text/nsTextEditRules.cpp | 3 - mozilla/editor/public/nsIEditor.h | 1 + 35 files changed, 795 insertions(+), 512 deletions(-) diff --git a/mozilla/editor/base/CreateElementTxn.cpp b/mozilla/editor/base/CreateElementTxn.cpp index 615e95acf33..9565713a237 100644 --- a/mozilla/editor/base/CreateElementTxn.cpp +++ b/mozilla/editor/base/CreateElementTxn.cpp @@ -80,13 +80,13 @@ NS_IMETHODIMP CreateElementTxn::Do(void) } NS_ASSERTION(mEditor && mParent, "bad state"); - if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; + if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; nsresult result; // create a new node nsCOMPtrdoc; result = mEditor->GetDocument(getter_AddRefs(doc)); - if (NS_FAILED(result)) return result; - if (!doc) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!doc) return NS_ERROR_NULL_POINTER; nsAutoString textNodeTag; result = nsEditor::GetTextNodeTag(textNodeTag); @@ -97,20 +97,20 @@ NS_IMETHODIMP CreateElementTxn::Do(void) const nsString stringData; nsCOMPtrnewTextNode; result = doc->CreateTextNode(stringData, getter_AddRefs(newTextNode)); - if (NS_FAILED(result)) return result; - if (!newTextNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!newTextNode) return NS_ERROR_NULL_POINTER; mNewNode = do_QueryInterface(newTextNode); } else { nsCOMPtrnewElement; result = doc->CreateElement(mTag, getter_AddRefs(newElement)); - if (NS_FAILED(result)) return result; - if (!newElement) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!newElement) return NS_ERROR_NULL_POINTER; mNewNode = do_QueryInterface(newElement); } NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); - if (!mNewNode) return NS_ERROR_NULL_POINTER; + if (!mNewNode) return NS_ERROR_NULL_POINTER; if (gNoisy) { printf(" newNode = %p\n", mNewNode.get()); } // insert the new node @@ -130,23 +130,31 @@ NS_IMETHODIMP CreateElementTxn::Do(void) if (mOffsetInParent>count) mOffsetInParent = count; result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); - if (NS_SUCCEEDED(result)) // note, it's ok for mRefNode to be null. that means append + if (NS_FAILED(result)) return result; // note, it's ok for mRefNode to be null. that means append + + result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + if (NS_FAILED(result)) return result; + + // only set selection to insertion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) { - result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); - if (NS_SUCCEEDED(result)) - { - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; - PRInt32 offset=0; - result = nsEditor::GetChildOffset(mNewNode, mParent, offset); - if (NS_FAILED(result)) return result; + PRInt32 offset=0; + result = nsEditor::GetChildOffset(mNewNode, mParent, offset); + if (NS_FAILED(result)) return result; - result = selection->Collapse(mParent, offset+1); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); - } + result = selection->Collapse(mParent, offset+1); + NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); + } + else + { + // do nothing - dom range gravity will adjust selection } } } @@ -158,25 +166,10 @@ NS_IMETHODIMP CreateElementTxn::Undo(void) if (gNoisy) { printf("Undo Create Element, mParent = %p, node = %p\n", mParent.get(), mNewNode.get()); } NS_ASSERTION(mEditor && mParent, "bad state"); - if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; + if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; nsCOMPtr resultNode; nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); - if (NS_SUCCEEDED(result)) - { - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - - PRInt32 offset=0; - if (mRefNode) { - result = nsEditor::GetChildOffset(mRefNode, mParent, offset); - if (NS_FAILED(result)) return result; - } - result = selection->Collapse(mParent, offset); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); - } return result; } @@ -184,7 +177,7 @@ NS_IMETHODIMP CreateElementTxn::Redo(void) { if (gNoisy) { printf("Redo Create Element\n"); } NS_ASSERTION(mEditor && mParent, "bad state"); - if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; + if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; // first, reset mNewNode so it has no attributes or content nsCOMPtrnodeAsText; @@ -198,19 +191,6 @@ NS_IMETHODIMP CreateElementTxn::Redo(void) // now, reinsert mNewNode 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_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - PRInt32 offset=0; - result = nsEditor::GetChildOffset(mNewNode, mParent, offset); - if (NS_FAILED(result)) return result; - - result = selection->Collapse(mParent, offset+1); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after redo of insert."); - } return result; } diff --git a/mozilla/editor/base/DeleteRangeTxn.cpp b/mozilla/editor/base/DeleteRangeTxn.cpp index 14d63c38f41..0ede732409e 100644 --- a/mozilla/editor/base/DeleteRangeTxn.cpp +++ b/mozilla/editor/base/DeleteRangeTxn.cpp @@ -115,7 +115,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) { if (gNoisy) { printf("Do Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent || !mEditor) - return NS_ERROR_NOT_INITIALIZED; + return NS_ERROR_NOT_INITIALIZED; nsresult result; // build the child transactions @@ -145,14 +145,23 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) result = EditAggregateTxn::Do(); } - if (NS_SUCCEEDED(result)) { - // set the resulting selection + if (NS_FAILED(result)) return result; + + // only set selection to deletion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) + { nsCOMPtr selection; result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; result = selection->Collapse(mStartParent, mStartOffset); } + else + { + // do nothing - dom range gravity will adjust selection + } return result; } @@ -161,22 +170,9 @@ NS_IMETHODIMP DeleteRangeTxn::Undo(void) { if (gNoisy) { printf("Undo Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent || !mEditor) - return NS_ERROR_NOT_INITIALIZED; + return NS_ERROR_NOT_INITIALIZED; nsresult result = EditAggregateTxn::Undo(); - - if (NS_SUCCEEDED(result)) - { - // set the resulting selection - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mStartParent, mStartOffset); - if (NS_FAILED(result)) return result; - result = selection->Extend(mEndParent, mEndOffset); - } - return result; } @@ -184,19 +180,9 @@ NS_IMETHODIMP DeleteRangeTxn::Redo(void) { if (gNoisy) { printf("Redo Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent || !mEditor) - return NS_ERROR_NOT_INITIALIZED; + return NS_ERROR_NOT_INITIALIZED; nsresult result = EditAggregateTxn::Redo(); - - if (NS_SUCCEEDED(result)) { - // set the resulting selection - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mStartParent, mStartOffset); - } - return result; } @@ -243,8 +229,8 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, { // if the node is a text node, then delete text content DeleteTextTxn *txn; result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)&txn); - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_NULL_POINTER; PRInt32 numToDel; if (aStartOffset==aEndOffset) @@ -259,8 +245,8 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, PRUint32 childCount; nsCOMPtr children; result = aStartParent->GetChildNodes(getter_AddRefs(children)); - if (NS_FAILED(result)) return result; - if (!children) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!children) return NS_ERROR_NULL_POINTER; children->GetLength(&childCount); NS_ASSERTION(aEndOffset<=childCount, "bad aEndOffset"); @@ -269,13 +255,13 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, { nsCOMPtr child; result = children->Item(i, getter_AddRefs(child)); - if (NS_FAILED(result)) return result; - if (!child) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!child) return NS_ERROR_NULL_POINTER; DeleteElementTxn *txn; result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)&txn); - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_NULL_POINTER; txn->Init(child); AppendChild(txn); @@ -311,8 +297,8 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, { DeleteTextTxn *txn; result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)&txn); - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_NULL_POINTER; txn->Init(mEditor, textNode, start, numToDelete); AppendChild(txn); @@ -335,8 +321,8 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween() nsnull, nsIContentIterator::GetIID(), getter_AddRefs(iter)); - if (NS_FAILED(result)) return result; - if (!iter) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!iter) return NS_ERROR_NULL_POINTER; result = iter->Init(mRange); if (NS_FAILED(result)) return result; @@ -347,13 +333,13 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween() nsCOMPtr content; result = iter->CurrentNode(getter_AddRefs(content)); node = do_QueryInterface(content); - if (NS_FAILED(result)) return result; - if (!node) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!node) return NS_ERROR_NULL_POINTER; DeleteElementTxn *txn; result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)&txn); - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_NULL_POINTER; txn->Init(node); AppendChild(txn); diff --git a/mozilla/editor/base/DeleteTextTxn.cpp b/mozilla/editor/base/DeleteTextTxn.cpp index 7d3c3125663..2452cde0406 100644 --- a/mozilla/editor/base/DeleteTextTxn.cpp +++ b/mozilla/editor/base/DeleteTextTxn.cpp @@ -71,14 +71,23 @@ NS_IMETHODIMP DeleteTextTxn::Do(void) nsresult 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)) + if (NS_FAILED(result)) return result; + + // only set selection to deletion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) { nsCOMPtr selection; result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; result = selection->Collapse(mElement, mOffset); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); + NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of deletetext."); + } + else + { + // do nothing - dom range gravity will adjust selection } return result; } @@ -93,15 +102,6 @@ NS_IMETHODIMP DeleteTextTxn::Undo(void) nsresult result; result = mElement->InsertData(mOffset, mDeletedText); - if (NS_SUCCEEDED(result)) - { - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mElement, mOffset); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); - } return result; } diff --git a/mozilla/editor/base/InsertElementTxn.cpp b/mozilla/editor/base/InsertElementTxn.cpp index 73a3d3f1e60..44adf84f1f5 100644 --- a/mozilla/editor/base/InsertElementTxn.cpp +++ b/mozilla/editor/base/InsertElementTxn.cpp @@ -103,16 +103,25 @@ NS_IMETHODIMP InsertElementTxn::Do(void) nsCOMPtr resultNode; result = mParent->InsertBefore(mNode, refNode, getter_AddRefs(resultNode)); - if (NS_FAILED(result)) return result; - if (!resultNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!resultNode) return NS_ERROR_NULL_POINTER; - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - // place the selection just after the inserted element - selection->Collapse(mParent, mOffset+1); - //selection->Extend(mParent, mOffset+1); + // only set selection to insertion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) + { + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + // place the selection just after the inserted element + selection->Collapse(mParent, mOffset+1); + } + else + { + // do nothing - dom range gravity will adjust selection + } return result; } diff --git a/mozilla/editor/base/InsertTextTxn.cpp b/mozilla/editor/base/InsertTextTxn.cpp index 1d16f81780e..2a238d456b2 100644 --- a/mozilla/editor/base/InsertTextTxn.cpp +++ b/mozilla/editor/base/InsertTextTxn.cpp @@ -64,7 +64,7 @@ InsertTextTxn::~InsertTextTxn() NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, const nsString &aStringToInsert, - nsWeakPtr aPresShellWeak) + nsIEditor *aEditor) { #if 0 //def DEBUG_cmanske nsAutoString text; @@ -74,58 +74,54 @@ NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, printf("\n"); #endif - NS_ASSERTION(aElement && aPresShellWeak, "bad args"); - if (!aElement || !aPresShellWeak) return NS_ERROR_NULL_POINTER; + NS_ASSERTION(aElement && aEditor, "bad args"); + if (!aElement || !aEditor) return NS_ERROR_NULL_POINTER; mElement = do_QueryInterface(aElement); mOffset = aOffset; mStringToInsert = aStringToInsert; - mPresShellWeak = aPresShellWeak; + mEditor = aEditor; return NS_OK; } NS_IMETHODIMP InsertTextTxn::Do(void) { if (gNoisy) { printf("Do Insert Text element = %p\n", mElement.get()); } - NS_ASSERTION(mElement && mPresShellWeak, "bad state"); - if (!mElement || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; } - nsCOMPtr ps = do_QueryReferent(mPresShellWeak); - if (!ps) return NS_ERROR_NOT_INITIALIZED; + NS_ASSERTION(mElement && mEditor, "bad state"); + if (!mElement || !mEditor) { return NS_ERROR_NOT_INITIALIZED; } - // advance caret: This requires the presentation shell to get the selection. - nsCOMPtr selection; - nsresult result = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + nsresult result = mElement->InsertData(mOffset, mStringToInsert); if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = mElement->InsertData(mOffset, mStringToInsert); - if (NS_SUCCEEDED(result)) + + // only set selection to insertion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) { + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; result = selection->Collapse(mElement, mOffset+mStringToInsert.Length()); NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); } + else + { + // do nothing - dom range gravity will adjust selection + } + return result; } NS_IMETHODIMP InsertTextTxn::Undo(void) { if (gNoisy) { printf("Undo Insert Text element = %p\n", mElement.get()); } - NS_ASSERTION(mElement && mPresShellWeak, "bad state"); - if (!mElement || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; } - nsCOMPtr ps = do_QueryReferent(mPresShellWeak); - if (!ps) return NS_ERROR_NOT_INITIALIZED; + NS_ASSERTION(mElement && mEditor, "bad state"); + if (!mElement || !mEditor) { return NS_ERROR_NOT_INITIALIZED; } nsresult result; PRUint32 length = mStringToInsert.Length(); result = mElement->DeleteData(mOffset, length); - if (NS_SUCCEEDED(result)) - { // set the selection to the insertion point where the string was removed - nsCOMPtr selection; - result = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mElement, mOffset); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); - } return result; } diff --git a/mozilla/editor/base/InsertTextTxn.h b/mozilla/editor/base/InsertTextTxn.h index 2b4471ebe23..60eebc3e7d6 100644 --- a/mozilla/editor/base/InsertTextTxn.h +++ b/mozilla/editor/base/InsertTextTxn.h @@ -25,8 +25,8 @@ #include "EditTxn.h" #include "nsIDOMCharacterData.h" +#include "nsIEditor.h" #include "nsCOMPtr.h" -#include "nsWeakPtr.h" #define INSERT_TEXT_TXN_CID \ {/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \ @@ -60,7 +60,7 @@ public: NS_IMETHOD Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, const nsString& aString, - nsWeakPtr aPresShellWeak); + nsIEditor *aEditor); private: @@ -110,8 +110,8 @@ protected: /** the text to insert into mElement at mOffset */ nsString mStringToInsert; - /** the presentation shell, which we'll need to get the selection */ - nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell + /** the editor, which we'll need to get the selection */ + nsIEditor *mEditor; friend class TransactionFactory; diff --git a/mozilla/editor/base/JoinElementTxn.cpp b/mozilla/editor/base/JoinElementTxn.cpp index 32988fd271c..e0fbc3f351c 100644 --- a/mozilla/editor/base/JoinElementTxn.cpp +++ b/mozilla/editor/base/JoinElementTxn.cpp @@ -24,7 +24,6 @@ #include "nsEditor.h" #include "nsIDOMNodeList.h" #include "nsIDOMCharacterData.h" -#include "nsIDOMSelection.h" #ifdef NS_DEBUG static PRBool gNoisy = PR_FALSE; @@ -39,12 +38,12 @@ JoinElementTxn::JoinElementTxn() /* log description initialized in parent constructor */ } -NS_IMETHODIMP JoinElementTxn::Init(nsIEditor *aEditor, +NS_IMETHODIMP JoinElementTxn::Init(nsEditor *aEditor, nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode) { - NS_PRECONDITION((aEditor && aLeftNode && aRightNode), "null arg"); - if (!aEditor || !aLeftNode || !aRightNode) { return NS_ERROR_NULL_POINTER; } + NS_PRECONDITION((aEditor && aLeftNode && aRightNode), "null arg"); + if (!aEditor || !aLeftNode || !aRightNode) { return NS_ERROR_NULL_POINTER; } mEditor = aEditor; mLeftNode = do_QueryInterface(aLeftNode); mRightNode = do_QueryInterface(aRightNode); @@ -60,20 +59,20 @@ JoinElementTxn::~JoinElementTxn() NS_IMETHODIMP JoinElementTxn::Do(void) { if (gNoisy) { printf("%p Do Join of %p and %p\n", this, mLeftNode.get(), mRightNode.get()); } - NS_PRECONDITION((mEditor && mLeftNode && mRightNode), "null arg"); - if (!mEditor || !mLeftNode || !mRightNode) { return NS_ERROR_NOT_INITIALIZED; } + NS_PRECONDITION((mEditor && mLeftNode && mRightNode), "null arg"); + if (!mEditor || !mLeftNode || !mRightNode) { return NS_ERROR_NOT_INITIALIZED; } // get the parent node nsCOMPtrleftParent; nsresult result = mLeftNode->GetParentNode(getter_AddRefs(leftParent)); - if (NS_FAILED(result)) return result; - if (!leftParent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!leftParent) return NS_ERROR_NULL_POINTER; // verify that mLeftNode and mRightNode have the same parent nsCOMPtrrightParent; result = mRightNode->GetParentNode(getter_AddRefs(rightParent)); - if (NS_FAILED(result)) return result; - if (!rightParent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!rightParent) return NS_ERROR_NULL_POINTER; if (leftParent==rightParent) { @@ -89,21 +88,16 @@ NS_IMETHODIMP JoinElementTxn::Do(void) { nsCOMPtr childNodes; result = mLeftNode->GetChildNodes(getter_AddRefs(childNodes)); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) return result; if (childNodes) { childNodes->GetLength(&mOffset); } } - result = nsEditor::JoinNodesImpl(mRightNode, mLeftNode, mParent, PR_FALSE); + result = mEditor->JoinNodesImpl(mRightNode, mLeftNode, mParent, PR_FALSE); if (NS_SUCCEEDED(result)) { if (gNoisy) { printf(" left node = %p removed\n", mLeftNode.get()); } - nsCOMPtrselection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - selection->Collapse(mRightNode, mOffset); } } else diff --git a/mozilla/editor/base/JoinElementTxn.h b/mozilla/editor/base/JoinElementTxn.h index d6af6af6988..8550de2e7a2 100644 --- a/mozilla/editor/base/JoinElementTxn.h +++ b/mozilla/editor/base/JoinElementTxn.h @@ -33,6 +33,7 @@ 0x9bc5f9f0, 0xac48, 0x11d2, \ {0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } +class nsEditor; /** * A transaction that joins two elements E1 (left node) and E2 (right node) @@ -51,7 +52,7 @@ public: * @param aLeftNode the first of two nodes to join * @param aRightNode the second of two nodes to join */ - NS_IMETHOD Init(nsIEditor *aEditor, + NS_IMETHOD Init(nsEditor *aEditor, nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode); protected: @@ -95,7 +96,7 @@ protected: /** the parent node containing mLeftNode and mRightNode */ nsCOMPtr mParent; - nsIEditor* mEditor; + nsEditor* mEditor; friend class TransactionFactory; diff --git a/mozilla/editor/base/SplitElementTxn.cpp b/mozilla/editor/base/SplitElementTxn.cpp index 7224a709182..513f8847f0a 100644 --- a/mozilla/editor/base/SplitElementTxn.cpp +++ b/mozilla/editor/base/SplitElementTxn.cpp @@ -41,12 +41,12 @@ SplitElementTxn::SplitElementTxn() /* log description initialized in parent constructor */ } -NS_IMETHODIMP SplitElementTxn::Init(nsIEditor *aEditor, +NS_IMETHODIMP SplitElementTxn::Init(nsEditor *aEditor, nsIDOMNode *aNode, PRInt32 aOffset) { - NS_ASSERTION(aEditor && aNode, "bad args"); - if (!aEditor || !aNode) { return NS_ERROR_NOT_INITIALIZED; } + NS_ASSERTION(aEditor && aNode, "bad args"); + if (!aEditor || !aNode) { return NS_ERROR_NOT_INITIALIZED; } mEditor = aEditor; mExistingRightNode = do_QueryInterface(aNode); @@ -67,17 +67,17 @@ NS_IMETHODIMP SplitElementTxn::Do(void) // create a new node nsresult result = mExistingRightNode->CloneNode(PR_FALSE, getter_AddRefs(mNewLeftNode)); NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element."); - if (NS_FAILED(result)) return result; - if (!mNewLeftNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!mNewLeftNode) return NS_ERROR_NULL_POINTER; if (gNoisy) { printf(" created left node = %p\n", mNewLeftNode.get()); } // get the parent node result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent)); - if (NS_FAILED(result)) return result; - if (!mParent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!mParent) return NS_ERROR_NULL_POINTER; // insert the new node - result = nsEditor::SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); + result = mEditor->SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); if (NS_SUCCEEDED(result) && mNewLeftNode) { // Insert formatting whitespace for the new node: @@ -85,8 +85,8 @@ NS_IMETHODIMP SplitElementTxn::Do(void) nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; result = selection->Collapse(mNewLeftNode, mOffset); } else { @@ -107,8 +107,7 @@ NS_IMETHODIMP SplitElementTxn::Undo(void) } // this assumes Do inserted the new node in front of the prior existing node - nsresult result; - result = nsEditor::JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); + nsresult result = mEditor->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); if (gNoisy) { printf("** after join left child node %p into right node %p\n", mNewLeftNode.get(), mExistingRightNode.get()); @@ -117,14 +116,6 @@ NS_IMETHODIMP SplitElementTxn::Undo(void) if (NS_SUCCEEDED(result)) { if (gNoisy) { printf(" left node = %p removed\n", mNewLeftNode.get()); } - nsCOMPtrselection; - mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mExistingRightNode, mOffset); - } - else { - result = NS_ERROR_NOT_IMPLEMENTED; } return result; } diff --git a/mozilla/editor/base/SplitElementTxn.h b/mozilla/editor/base/SplitElementTxn.h index 6ac3f9b3073..0e03c5831d7 100644 --- a/mozilla/editor/base/SplitElementTxn.h +++ b/mozilla/editor/base/SplitElementTxn.h @@ -33,6 +33,8 @@ 0x690c6290, 0xac48, 0x11d2, \ {0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } +class nsEditor; + /** * A transaction that splits an element E into two identical nodes, E1 and E2 * with the children of E divided between E1 and E2. @@ -50,7 +52,7 @@ public: * aOffset may refer to children of aNode, or content of aNode. * The left node will have child|content 0..aOffset-1. */ - NS_IMETHOD Init (nsIEditor *aEditor, + NS_IMETHOD Init (nsEditor *aEditor, nsIDOMNode *aNode, PRInt32 aOffset); protected: @@ -93,7 +95,7 @@ protected: /** the parent shared by mExistingRightNode and mNewLeftNode */ nsCOMPtr mParent; - nsIEditor* mEditor; + nsEditor* mEditor; friend class TransactionFactory; diff --git a/mozilla/editor/base/nsEditor.cpp b/mozilla/editor/base/nsEditor.cpp index 2ee52d1f27f..90844187392 100644 --- a/mozilla/editor/base/nsEditor.cpp +++ b/mozilla/editor/base/nsEditor.cpp @@ -632,6 +632,14 @@ nsEditor::EndPlaceHolderTransaction() return NS_OK; } +NS_IMETHODIMP nsEditor::ShouldTxnSetSelection(PRBool *aResult) +{ + if (!aResult) return NS_ERROR_NULL_POINTER; + *aResult = mShouldTxnSetSelection; + return NS_OK; +} + + // XXX: the rule system should tell us which node to select all on (ie, the root, or the body) NS_IMETHODIMP nsEditor::SelectAll() { @@ -1743,24 +1751,24 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) if (NS_SUCCEEDED(result)) { - EditTxn *txn; - if (mInIMEMode) - { - if (!mIMETextNode) - { + EditTxn *txn; + if (mInIMEMode) + { + if (!mIMETextNode) + { mIMETextNode = nodeAsText; mIMETextOffset = offset; } - result = CreateTxnForIMEText(aStringToInsert,(IMETextTxn**)&txn); - } - else - { - result = CreateTxnForInsertText(aStringToInsert, nodeAsText, offset, (InsertTextTxn**)&txn); - } - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_OUT_OF_MEMORY; - - // let listeners know whats up + result = CreateTxnForIMEText(aStringToInsert,(IMETextTxn**)&txn); + } + else + { + result = CreateTxnForInsertText(aStringToInsert, nodeAsText, offset, (InsertTextTxn**)&txn); + } + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_OUT_OF_MEMORY; + + // let listeners know whats up PRInt32 i; nsIEditActionListener *listener; if (mActionListeners) @@ -1780,7 +1788,7 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) NS_IF_RELEASE(txn); EndUpdateViewBatch(); - // let listeners know what happened + // let listeners know what happened if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -2024,10 +2032,11 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert, result = TransactionFactory::GetNewTransaction(InsertTextTxn::GetCID(), (EditTxn **)aTxn); if (NS_FAILED(result)) return result; if (!*aTxn) return NS_ERROR_OUT_OF_MEMORY; - result = (*aTxn)->Init(aTextNode, aOffset, aStringToInsert, mPresShellWeak); + result = (*aTxn)->Init(aTextNode, aOffset, aStringToInsert, this); return result; } + NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aLength) @@ -2037,7 +2046,7 @@ NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, nsAutoRules beginRulesSniffing(this, kOpDeleteText, nsIEditor::ePrevious); if (NS_SUCCEEDED(result)) { - // let listeners know whats up + // let listeners know whats up PRInt32 i; nsIEditActionListener *listener; if (mActionListeners) @@ -2052,7 +2061,7 @@ NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, result = Do(txn); - // let listeners know what happened + // let listeners know what happened if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -2127,7 +2136,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, #pragma mark - #endif -// BEGIN nsEditor public static helper methods +// BEGIN nsEditor public helper methods nsresult nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, @@ -2147,6 +2156,20 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, (nsnull!=aNewLeftNode) && (nsnull!=aParent)) { + // get selection + nsCOMPtr selection; + GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + // remember some selection points + nsCOMPtr selStartNode, selEndNode; + PRInt32 selStartOffset, selEndOffset; + result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset); + if (NS_FAILED(result)) return result; + result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset); + if (NS_FAILED(result)) return result; + nsCOMPtr resultNode; result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode)); //printf(" after insert\n"); content->List(); // DEBUG @@ -2197,6 +2220,41 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, } } } + // handle selection + if (GetShouldTxnSetSelection()) + { + // editor wants us to set selection at split point + selection->Collapse(aNewLeftNode, aOffset); + } + else + { + // and adjust the selection if needed + // HACK: this is overly simplified - multi-range selections need more work than this + if (selStartNode == aExistingRightNode) + { + if (selStartOffset < aOffset) + { + selStartNode = aNewLeftNode; + } + else + { + selStartOffset -= aOffset; + } + } + if (selEndNode == aExistingRightNode) + { + if (selEndOffset < aOffset) + { + selEndNode = aNewLeftNode; + } + else + { + selEndOffset -= aOffset; + } + } + selection->Collapse(selStartNode,selStartOffset); + selection->Extend(selEndNode,selEndOffset); + } } } } @@ -2213,14 +2271,28 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, PRBool aNodeToKeepIsFirst) { nsresult result = NS_OK; - NS_ASSERTION(((nsnull!=aNodeToKeep) && - (nsnull!=aNodeToJoin) && - (nsnull!=aParent)), - "null arg"); - if ((nsnull!=aNodeToKeep) && - (nsnull!=aNodeToJoin) && - (nsnull!=aParent)) + NS_ASSERTION(aNodeToKeep && aNodeToJoin && aParent, "null arg"); + if (aNodeToKeep && aNodeToJoin && aParent) { + // get selection + nsCOMPtr selection; + GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + // remember some selection points + nsCOMPtr selStartNode, selEndNode; + PRInt32 selStartOffset, selEndOffset; + result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset); + if (NS_FAILED(result)) return result; + result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset); + if (NS_FAILED(result)) return result; + PRUint32 firstNodeLength; + nsCOMPtr leftNode = aNodeToJoin; + if (aNodeToKeepIsFirst) leftNode = aNodeToKeep; + result = GetLengthOfDOMNode(leftNode, firstNodeLength); + if (NS_FAILED(result)) return result; + // if it's a text node, just shuffle around some text nsCOMPtr keepNodeAsText( do_QueryInterface(aNodeToKeep) ); nsCOMPtr joinNodeAsText( do_QueryInterface(aNodeToJoin) ); @@ -2249,9 +2321,9 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, { PRInt32 i; // must be signed int! PRUint32 childCount=0; - childNodes->GetLength(&childCount); nsCOMPtr firstNode; //only used if aNodeToKeepIsFirst is false - if (PR_FALSE==aNodeToKeepIsFirst) + childNodes->GetLength(&childCount); + if (!aNodeToKeepIsFirst) { // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it result = aNodeToKeep->GetFirstChild(getter_AddRefs(firstNode)); // GetFirstChild returns nsnull firstNode if aNodeToKeep has no children, that's ok. @@ -2265,7 +2337,7 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, result = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(result)) && (childNode)) { - if (PR_TRUE==aNodeToKeepIsFirst) + if (aNodeToKeepIsFirst) { // append children of aNodeToJoin //was result = aNodeToKeep->AppendChild(childNode, getter_AddRefs(resultNode)); result = aNodeToKeep->InsertBefore(childNode, previousChild, getter_AddRefs(resultNode)); @@ -2288,6 +2360,35 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, { // delete the extra node nsCOMPtr resultNode; result = aParent->RemoveChild(aNodeToJoin, getter_AddRefs(resultNode)); + + if (GetShouldTxnSetSelection()) + { + // editor wants us to set selection at join point + selection->Collapse(aNodeToKeep, firstNodeLength); + } + else + { + // and adjust the selection if needed + // HACK: this is overly simplified - multi-range selections need more work than this + if (selStartNode == aNodeToJoin) + { + selStartNode = aNodeToKeep; + if (aNodeToKeepIsFirst) + { + selStartOffset += firstNodeLength; + } + } + if (selEndNode == aNodeToJoin) + { + selEndNode = aNodeToKeep; + if (aNodeToKeepIsFirst) + { + selEndOffset += firstNodeLength; + } + } + selection->Collapse(selStartNode,selStartOffset); + selection->Extend(selEndNode,selEndOffset); + } } } else @@ -3998,13 +4099,13 @@ nsresult nsEditor::EndUpdateViewBatch() { NS_PRECONDITION(mUpdateCount>0, "bad state"); - nsCOMPtr presShell; + nsCOMPtr presShell; nsresult rv = GetPresShell(getter_AddRefs(presShell)); if (NS_FAILED(rv)) return rv; StCaretHider caretHider(presShell); - + nsCOMPtrselection; nsresult selectionResult = GetSelection(getter_AddRefs(selection)); if (NS_SUCCEEDED(selectionResult) && selection) { @@ -4028,6 +4129,18 @@ nsresult nsEditor::EndUpdateViewBatch() return NS_OK; } +PRBool +nsEditor::GetShouldTxnSetSelection() +{ + return mShouldTxnSetSelection; +} + + +void +nsEditor::SetShouldTxnSetSelection(PRBool aShould) +{ + mShouldTxnSetSelection = aShould; +} #ifdef XP_MAC #pragma mark - @@ -4270,7 +4383,7 @@ nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert, { NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn"); if(!aTxn) return NS_ERROR_NULL_POINTER; - + nsresult result; result = TransactionFactory::GetNewTransaction(IMETextTxn::GetCID(), (EditTxn **)aTxn); diff --git a/mozilla/editor/base/nsEditor.h b/mozilla/editor/base/nsEditor.h index 344976c90c7..2241d5f4a3f 100644 --- a/mozilla/editor/base/nsEditor.h +++ b/mozilla/editor/base/nsEditor.h @@ -142,6 +142,7 @@ public: NS_IMETHOD BeginPlaceHolderTransaction(nsIAtom *aName); NS_IMETHOD EndPlaceHolderTransaction(); + NS_IMETHOD ShouldTxnSetSelection(PRBool *aResult); // pure virtual, because the definition of 'empty' depends on the doc type NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty)=0; @@ -396,10 +397,10 @@ public: * @param aNewLeftNode [OUT] the new node resulting from the split, becomes aExistingRightNode's previous sibling. * @param aParent the parent of aExistingRightNode */ - static nsresult SplitNodeImpl(nsIDOMNode *aExistingRightNode, - PRInt32 aOffset, - nsIDOMNode *aNewLeftNode, - nsIDOMNode *aParent); + nsresult SplitNodeImpl(nsIDOMNode *aExistingRightNode, + PRInt32 aOffset, + nsIDOMNode *aNewLeftNode, + nsIDOMNode *aParent); /** * JoinNodes() takes 2 nodes and merge their content|children. @@ -410,10 +411,10 @@ public: * @param aNodeToKeepIsFirst if PR_TRUE, the contents|children of aNodeToKeep come before the * contents|children of aNodeToJoin, otherwise their positions are switched. */ - static nsresult JoinNodesImpl(nsIDOMNode *aNodeToKeep, - nsIDOMNode *aNodeToJoin, - nsIDOMNode *aParent, - PRBool aNodeToKeepIsFirst); + nsresult JoinNodesImpl(nsIDOMNode *aNodeToKeep, + nsIDOMNode *aNodeToJoin, + nsIDOMNode *aParent, + PRBool aNodeToKeepIsFirst); /** * Set aOffset to the offset of aChild in aParent. @@ -629,6 +630,8 @@ public: nsresult BeginUpdateViewBatch(void); nsresult EndUpdateViewBatch(void); + PRBool GetShouldTxnSetSelection(); + void SetShouldTxnSetSelection(PRBool aShould); protected: @@ -645,7 +648,7 @@ protected: PRInt32 mPlaceHolderBatch; // nesting count for batching nsCOMPtr mTxnStartNode; // saved selection info to pass to placeholder at init time PRInt32 mTxnStartOffset; // " " " " - + PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns // // data necessary to build IME transactions // @@ -666,6 +669,7 @@ protected: static PRInt32 gInstanceCount; friend PRBool NSCanUnload(nsISupports* serviceMgr); + friend class nsAutoTxnsConserveSelection; }; diff --git a/mozilla/editor/base/nsEditorUtils.h b/mozilla/editor/base/nsEditorUtils.h index e70472714a8..be12960f5b3 100644 --- a/mozilla/editor/base/nsEditorUtils.h +++ b/mozilla/editor/base/nsEditorUtils.h @@ -107,5 +107,36 @@ class nsAutoRules }; +/*************************************************************************** + * stack based helper class for turning off active selection adjustment + * by low level transactions + */ +class nsAutoTxnsConserveSelection +{ + public: + + nsAutoTxnsConserveSelection(nsEditor *ed) : mEd(ed), mOldState(PR_TRUE) + { + if (mEd) + { + mOldState = mEd->GetShouldTxnSetSelection(); + mEd->SetShouldTxnSetSelection(PR_FALSE); + } + } + + ~nsAutoTxnsConserveSelection() + { + if (mEd) + { + mEd->SetShouldTxnSetSelection(mOldState); + } + } + + protected: + nsEditor *mEd; + PRBool mOldState; +}; + + #endif // nsEditorUtils_h__ diff --git a/mozilla/editor/base/nsHTMLEditRules.cpp b/mozilla/editor/base/nsHTMLEditRules.cpp index e01f1a5a353..876ff6eddb3 100644 --- a/mozilla/editor/base/nsHTMLEditRules.cpp +++ b/mozilla/editor/base/nsHTMLEditRules.cpp @@ -153,6 +153,8 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) { + nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); + // expand the "changed doc range" as needed res = PromoteRange(mDocChangeRange, action); if (NS_FAILED(res)) return res; @@ -2687,7 +2689,7 @@ nsHTMLEditRules::ReplaceContainer(nsIDOMNode *inNode, // the parent of inNode // nsresult -nsHTMLEditRules::RemoveContainer(nsIDOMNode *inNode) +nsHTMLEditRules::RemoveContainer(nsIDOMNode *inNode, PRBool aAddBRIfNeeded) { if (!inNode) return NS_ERROR_NULL_POINTER; @@ -2698,6 +2700,38 @@ nsHTMLEditRules::RemoveContainer(nsIDOMNode *inNode) PRInt32 offset; res = nsEditor::GetNodeLocation(inNode, &parent, &offset); if (NS_FAILED(res)) return res; + + // add BR's before and/or after the inNode + // if aAddBRIfNeeded is set and if the content + // before/after the node is inline. + // Only do this if inNode is a block node. + if (aAddBRIfNeeded && mEditor->IsBlockNode(inNode)) + { + nsCOMPtr nearNode, brNode; + res = GetPriorHTMLSibling(inNode, &nearNode); + if (NS_FAILED(res)) return res; + if (nearNode && mEditor->IsInlineNode(nearNode)) + { + res = mEditor->CreateBR(parent, offset, &brNode); + if (NS_FAILED(res)) return res; + // refresh location info + res = nsEditor::GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + } + res = GetNextHTMLSibling(inNode, &nearNode); + if (NS_FAILED(res)) return res; + if (nearNode && mEditor->IsInlineNode(nearNode)) + { + res = mEditor->CreateBR(parent, offset+1, &brNode); + if (NS_FAILED(res)) return res; + // refresh location info + res = nsEditor::GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + } + } + + // loop through the child nodes of inNode and promote them + // into inNode's parent. PRBool bHasMoreChildren; inNode->HasChildNodes(&bHasMoreChildren); nsCOMPtr child; @@ -3249,7 +3283,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString // we special case an empty tag name to mean "remove block parents". // This is used for the "normal" paragraph style in mail-compose - if (aBlockTag->IsEmpty()) bNoParent = PR_TRUE; + if (aBlockTag->IsEmpty() || *aBlockTag=="normal") bNoParent = PR_TRUE; arrayOfNodes->Count(&listCount); @@ -3272,11 +3306,6 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString continue; // do nothing to this block } - // if curNode is a
 and we are converting to non-pre, we need
-    // to process the text inside the 
 so as to convert returns
-    // to breaks, and runs of spaces to nbsps.
-    // xxx floppy moose
-      
     // if curNode is a mozdiv, p, header, address, or pre, replace 
     // it with a new block of correct type.
     // xxx floppy moose: pre cant hold everything the others can
@@ -3294,10 +3323,10 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
       curBlock = 0;  // forget any previous block used for previous inline nodes
       if (bNoParent)
       {
-        nsCOMPtr brNode;
-        res = mEditor->CreateBR(curParent, offset+1, &brNode);
-        if (NS_FAILED(res)) return res;
-        res = RemoveContainer(curNode);
+//        nsCOMPtr brNode;
+//        res = mEditor->CreateBR(curParent, offset+1, &brNode);
+//        if (NS_FAILED(res)) return res;
+        res = RemoveContainer(curNode, PR_TRUE); 
       }
       else
       {
@@ -4029,10 +4058,22 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
   // unwrap list item contents if they are no longer in a list
   if (!IsList(curParPar) && IsListItem(curNode)) 
   {
-    nsCOMPtr mozDiv;
+    nsCOMPtr lastChild;
+    res = GetLastEditableChild(curNode, &lastChild);
+    if (NS_FAILED(res)) return res;
     res = RemoveContainer(curNode);
     if (NS_FAILED(res)) return res;
-
+    if (mEditor->IsInlineNode(lastChild))
+    {
+      // last thing inside of the listitem wasn't a block node.
+      // insert a BR to preserve the illusion of block boundaries
+      nsCOMPtr node, brNode;
+      PRInt32 offset;
+      res = nsEditor::GetNodeLocation(lastChild, &node, &offset);
+      if (NS_FAILED(res)) return res;
+      res = mEditor->CreateBR(node, offset+1, &brNode);
+      if (NS_FAILED(res)) return res;
+    }
     *aOutOfList = PR_TRUE;
   }
   return res;
diff --git a/mozilla/editor/base/nsHTMLEditRules.h b/mozilla/editor/base/nsHTMLEditRules.h
index da8b675f810..51aac544b99 100644
--- a/mozilla/editor/base/nsHTMLEditRules.h
+++ b/mozilla/editor/base/nsHTMLEditRules.h
@@ -133,7 +133,7 @@ protected:
   nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag);
 
   nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType);
-  nsresult RemoveContainer(nsIDOMNode *inNode);
+  nsresult RemoveContainer(nsIDOMNode *inNode, PRBool aAddBRIfNeeded=PR_FALSE);
   nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType);
 
   nsresult JoinNodesSmart( nsIDOMNode *aNodeLeft, 
diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp
index bf452fa81c2..aef7b9bb128 100644
--- a/mozilla/editor/base/nsHTMLEditor.cpp
+++ b/mozilla/editor/base/nsHTMLEditor.cpp
@@ -1818,13 +1818,9 @@ NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat)
   //Kinda sad to waste memory just to force lower case
   nsAutoString tag = aParagraphFormat;
   tag.ToLowerCase();
-  if (tag == "normal") 
-  {
-    res = InsertBasicBlock("p");
-  } 
   //XXX: TODO: Do we really want to support setting list types here?
   // If yes, we need to add "dd", "dt" for 
support - else if (tag == "li") + if (tag == "li") { //XXX: Why do we assume "ul" What about "ol"? This will change an OL into a UL list! res = MakeOrChangeList("ul"); diff --git a/mozilla/editor/base/nsTextEditRules.cpp b/mozilla/editor/base/nsTextEditRules.cpp index 6b11e9fa013..00ee5a36829 100644 --- a/mozilla/editor/base/nsTextEditRules.cpp +++ b/mozilla/editor/base/nsTextEditRules.cpp @@ -1080,9 +1080,6 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, res = mEditor->JoinNodes(selectedNode, siblingNode, parentNode); if (NS_FAILED(res)) return res; // selectedNode will remain after the join, siblingNode is removed - // set selection - res = aSelection->Collapse(siblingNode, selectedNodeLength); - if (NS_FAILED(res)) return res; } } // if, after all this work, selectedNode is empty, delete it diff --git a/mozilla/editor/libeditor/base/CreateElementTxn.cpp b/mozilla/editor/libeditor/base/CreateElementTxn.cpp index 615e95acf33..9565713a237 100644 --- a/mozilla/editor/libeditor/base/CreateElementTxn.cpp +++ b/mozilla/editor/libeditor/base/CreateElementTxn.cpp @@ -80,13 +80,13 @@ NS_IMETHODIMP CreateElementTxn::Do(void) } NS_ASSERTION(mEditor && mParent, "bad state"); - if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; + if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; nsresult result; // create a new node nsCOMPtrdoc; result = mEditor->GetDocument(getter_AddRefs(doc)); - if (NS_FAILED(result)) return result; - if (!doc) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!doc) return NS_ERROR_NULL_POINTER; nsAutoString textNodeTag; result = nsEditor::GetTextNodeTag(textNodeTag); @@ -97,20 +97,20 @@ NS_IMETHODIMP CreateElementTxn::Do(void) const nsString stringData; nsCOMPtrnewTextNode; result = doc->CreateTextNode(stringData, getter_AddRefs(newTextNode)); - if (NS_FAILED(result)) return result; - if (!newTextNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!newTextNode) return NS_ERROR_NULL_POINTER; mNewNode = do_QueryInterface(newTextNode); } else { nsCOMPtrnewElement; result = doc->CreateElement(mTag, getter_AddRefs(newElement)); - if (NS_FAILED(result)) return result; - if (!newElement) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!newElement) return NS_ERROR_NULL_POINTER; mNewNode = do_QueryInterface(newElement); } NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); - if (!mNewNode) return NS_ERROR_NULL_POINTER; + if (!mNewNode) return NS_ERROR_NULL_POINTER; if (gNoisy) { printf(" newNode = %p\n", mNewNode.get()); } // insert the new node @@ -130,23 +130,31 @@ NS_IMETHODIMP CreateElementTxn::Do(void) if (mOffsetInParent>count) mOffsetInParent = count; result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode)); - if (NS_SUCCEEDED(result)) // note, it's ok for mRefNode to be null. that means append + if (NS_FAILED(result)) return result; // note, it's ok for mRefNode to be null. that means append + + result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); + if (NS_FAILED(result)) return result; + + // only set selection to insertion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) { - result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); - if (NS_SUCCEEDED(result)) - { - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; - PRInt32 offset=0; - result = nsEditor::GetChildOffset(mNewNode, mParent, offset); - if (NS_FAILED(result)) return result; + PRInt32 offset=0; + result = nsEditor::GetChildOffset(mNewNode, mParent, offset); + if (NS_FAILED(result)) return result; - result = selection->Collapse(mParent, offset+1); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); - } + result = selection->Collapse(mParent, offset+1); + NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); + } + else + { + // do nothing - dom range gravity will adjust selection } } } @@ -158,25 +166,10 @@ NS_IMETHODIMP CreateElementTxn::Undo(void) if (gNoisy) { printf("Undo Create Element, mParent = %p, node = %p\n", mParent.get(), mNewNode.get()); } NS_ASSERTION(mEditor && mParent, "bad state"); - if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; + if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; nsCOMPtr resultNode; nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); - if (NS_SUCCEEDED(result)) - { - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - - PRInt32 offset=0; - if (mRefNode) { - result = nsEditor::GetChildOffset(mRefNode, mParent, offset); - if (NS_FAILED(result)) return result; - } - result = selection->Collapse(mParent, offset); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); - } return result; } @@ -184,7 +177,7 @@ NS_IMETHODIMP CreateElementTxn::Redo(void) { if (gNoisy) { printf("Redo Create Element\n"); } NS_ASSERTION(mEditor && mParent, "bad state"); - if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; + if (!mEditor || !mParent) return NS_ERROR_NOT_INITIALIZED; // first, reset mNewNode so it has no attributes or content nsCOMPtrnodeAsText; @@ -198,19 +191,6 @@ NS_IMETHODIMP CreateElementTxn::Redo(void) // now, reinsert mNewNode 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_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - PRInt32 offset=0; - result = nsEditor::GetChildOffset(mNewNode, mParent, offset); - if (NS_FAILED(result)) return result; - - result = selection->Collapse(mParent, offset+1); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after redo of insert."); - } return result; } diff --git a/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp b/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp index 14d63c38f41..0ede732409e 100644 --- a/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp @@ -115,7 +115,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) { if (gNoisy) { printf("Do Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent || !mEditor) - return NS_ERROR_NOT_INITIALIZED; + return NS_ERROR_NOT_INITIALIZED; nsresult result; // build the child transactions @@ -145,14 +145,23 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) result = EditAggregateTxn::Do(); } - if (NS_SUCCEEDED(result)) { - // set the resulting selection + if (NS_FAILED(result)) return result; + + // only set selection to deletion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) + { nsCOMPtr selection; result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; result = selection->Collapse(mStartParent, mStartOffset); } + else + { + // do nothing - dom range gravity will adjust selection + } return result; } @@ -161,22 +170,9 @@ NS_IMETHODIMP DeleteRangeTxn::Undo(void) { if (gNoisy) { printf("Undo Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent || !mEditor) - return NS_ERROR_NOT_INITIALIZED; + return NS_ERROR_NOT_INITIALIZED; nsresult result = EditAggregateTxn::Undo(); - - if (NS_SUCCEEDED(result)) - { - // set the resulting selection - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mStartParent, mStartOffset); - if (NS_FAILED(result)) return result; - result = selection->Extend(mEndParent, mEndOffset); - } - return result; } @@ -184,19 +180,9 @@ NS_IMETHODIMP DeleteRangeTxn::Redo(void) { if (gNoisy) { printf("Redo Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent || !mEditor) - return NS_ERROR_NOT_INITIALIZED; + return NS_ERROR_NOT_INITIALIZED; nsresult result = EditAggregateTxn::Redo(); - - if (NS_SUCCEEDED(result)) { - // set the resulting selection - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mStartParent, mStartOffset); - } - return result; } @@ -243,8 +229,8 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, { // if the node is a text node, then delete text content DeleteTextTxn *txn; result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)&txn); - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_NULL_POINTER; PRInt32 numToDel; if (aStartOffset==aEndOffset) @@ -259,8 +245,8 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, PRUint32 childCount; nsCOMPtr children; result = aStartParent->GetChildNodes(getter_AddRefs(children)); - if (NS_FAILED(result)) return result; - if (!children) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!children) return NS_ERROR_NULL_POINTER; children->GetLength(&childCount); NS_ASSERTION(aEndOffset<=childCount, "bad aEndOffset"); @@ -269,13 +255,13 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, { nsCOMPtr child; result = children->Item(i, getter_AddRefs(child)); - if (NS_FAILED(result)) return result; - if (!child) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!child) return NS_ERROR_NULL_POINTER; DeleteElementTxn *txn; result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)&txn); - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_NULL_POINTER; txn->Init(child); AppendChild(txn); @@ -311,8 +297,8 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, { DeleteTextTxn *txn; result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)&txn); - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_NULL_POINTER; txn->Init(mEditor, textNode, start, numToDelete); AppendChild(txn); @@ -335,8 +321,8 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween() nsnull, nsIContentIterator::GetIID(), getter_AddRefs(iter)); - if (NS_FAILED(result)) return result; - if (!iter) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!iter) return NS_ERROR_NULL_POINTER; result = iter->Init(mRange); if (NS_FAILED(result)) return result; @@ -347,13 +333,13 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween() nsCOMPtr content; result = iter->CurrentNode(getter_AddRefs(content)); node = do_QueryInterface(content); - if (NS_FAILED(result)) return result; - if (!node) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!node) return NS_ERROR_NULL_POINTER; DeleteElementTxn *txn; result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)&txn); - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_NULL_POINTER; txn->Init(node); AppendChild(txn); diff --git a/mozilla/editor/libeditor/base/DeleteTextTxn.cpp b/mozilla/editor/libeditor/base/DeleteTextTxn.cpp index 7d3c3125663..2452cde0406 100644 --- a/mozilla/editor/libeditor/base/DeleteTextTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteTextTxn.cpp @@ -71,14 +71,23 @@ NS_IMETHODIMP DeleteTextTxn::Do(void) nsresult 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)) + if (NS_FAILED(result)) return result; + + // only set selection to deletion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) { nsCOMPtr selection; result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; result = selection->Collapse(mElement, mOffset); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); + NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of deletetext."); + } + else + { + // do nothing - dom range gravity will adjust selection } return result; } @@ -93,15 +102,6 @@ NS_IMETHODIMP DeleteTextTxn::Undo(void) nsresult result; result = mElement->InsertData(mOffset, mDeletedText); - if (NS_SUCCEEDED(result)) - { - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mElement, mOffset); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); - } return result; } diff --git a/mozilla/editor/libeditor/base/InsertElementTxn.cpp b/mozilla/editor/libeditor/base/InsertElementTxn.cpp index 73a3d3f1e60..44adf84f1f5 100644 --- a/mozilla/editor/libeditor/base/InsertElementTxn.cpp +++ b/mozilla/editor/libeditor/base/InsertElementTxn.cpp @@ -103,16 +103,25 @@ NS_IMETHODIMP InsertElementTxn::Do(void) nsCOMPtr resultNode; result = mParent->InsertBefore(mNode, refNode, getter_AddRefs(resultNode)); - if (NS_FAILED(result)) return result; - if (!resultNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!resultNode) return NS_ERROR_NULL_POINTER; - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - // place the selection just after the inserted element - selection->Collapse(mParent, mOffset+1); - //selection->Extend(mParent, mOffset+1); + // only set selection to insertion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) + { + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + // place the selection just after the inserted element + selection->Collapse(mParent, mOffset+1); + } + else + { + // do nothing - dom range gravity will adjust selection + } return result; } diff --git a/mozilla/editor/libeditor/base/InsertTextTxn.cpp b/mozilla/editor/libeditor/base/InsertTextTxn.cpp index 1d16f81780e..2a238d456b2 100644 --- a/mozilla/editor/libeditor/base/InsertTextTxn.cpp +++ b/mozilla/editor/libeditor/base/InsertTextTxn.cpp @@ -64,7 +64,7 @@ InsertTextTxn::~InsertTextTxn() NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, const nsString &aStringToInsert, - nsWeakPtr aPresShellWeak) + nsIEditor *aEditor) { #if 0 //def DEBUG_cmanske nsAutoString text; @@ -74,58 +74,54 @@ NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, printf("\n"); #endif - NS_ASSERTION(aElement && aPresShellWeak, "bad args"); - if (!aElement || !aPresShellWeak) return NS_ERROR_NULL_POINTER; + NS_ASSERTION(aElement && aEditor, "bad args"); + if (!aElement || !aEditor) return NS_ERROR_NULL_POINTER; mElement = do_QueryInterface(aElement); mOffset = aOffset; mStringToInsert = aStringToInsert; - mPresShellWeak = aPresShellWeak; + mEditor = aEditor; return NS_OK; } NS_IMETHODIMP InsertTextTxn::Do(void) { if (gNoisy) { printf("Do Insert Text element = %p\n", mElement.get()); } - NS_ASSERTION(mElement && mPresShellWeak, "bad state"); - if (!mElement || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; } - nsCOMPtr ps = do_QueryReferent(mPresShellWeak); - if (!ps) return NS_ERROR_NOT_INITIALIZED; + NS_ASSERTION(mElement && mEditor, "bad state"); + if (!mElement || !mEditor) { return NS_ERROR_NOT_INITIALIZED; } - // advance caret: This requires the presentation shell to get the selection. - nsCOMPtr selection; - nsresult result = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); + nsresult result = mElement->InsertData(mOffset, mStringToInsert); if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = mElement->InsertData(mOffset, mStringToInsert); - if (NS_SUCCEEDED(result)) + + // only set selection to insertion point if editor gives permission + PRBool bAdjustSelection; + mEditor->ShouldTxnSetSelection(&bAdjustSelection); + if (bAdjustSelection) { + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; result = selection->Collapse(mElement, mOffset+mStringToInsert.Length()); NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); } + else + { + // do nothing - dom range gravity will adjust selection + } + return result; } NS_IMETHODIMP InsertTextTxn::Undo(void) { if (gNoisy) { printf("Undo Insert Text element = %p\n", mElement.get()); } - NS_ASSERTION(mElement && mPresShellWeak, "bad state"); - if (!mElement || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; } - nsCOMPtr ps = do_QueryReferent(mPresShellWeak); - if (!ps) return NS_ERROR_NOT_INITIALIZED; + NS_ASSERTION(mElement && mEditor, "bad state"); + if (!mElement || !mEditor) { return NS_ERROR_NOT_INITIALIZED; } nsresult result; PRUint32 length = mStringToInsert.Length(); result = mElement->DeleteData(mOffset, length); - if (NS_SUCCEEDED(result)) - { // set the selection to the insertion point where the string was removed - nsCOMPtr selection; - result = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mElement, mOffset); - NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of insert."); - } return result; } diff --git a/mozilla/editor/libeditor/base/InsertTextTxn.h b/mozilla/editor/libeditor/base/InsertTextTxn.h index 2b4471ebe23..60eebc3e7d6 100644 --- a/mozilla/editor/libeditor/base/InsertTextTxn.h +++ b/mozilla/editor/libeditor/base/InsertTextTxn.h @@ -25,8 +25,8 @@ #include "EditTxn.h" #include "nsIDOMCharacterData.h" +#include "nsIEditor.h" #include "nsCOMPtr.h" -#include "nsWeakPtr.h" #define INSERT_TEXT_TXN_CID \ {/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \ @@ -60,7 +60,7 @@ public: NS_IMETHOD Init(nsIDOMCharacterData *aElement, PRUint32 aOffset, const nsString& aString, - nsWeakPtr aPresShellWeak); + nsIEditor *aEditor); private: @@ -110,8 +110,8 @@ protected: /** the text to insert into mElement at mOffset */ nsString mStringToInsert; - /** the presentation shell, which we'll need to get the selection */ - nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell + /** the editor, which we'll need to get the selection */ + nsIEditor *mEditor; friend class TransactionFactory; diff --git a/mozilla/editor/libeditor/base/JoinElementTxn.cpp b/mozilla/editor/libeditor/base/JoinElementTxn.cpp index 32988fd271c..e0fbc3f351c 100644 --- a/mozilla/editor/libeditor/base/JoinElementTxn.cpp +++ b/mozilla/editor/libeditor/base/JoinElementTxn.cpp @@ -24,7 +24,6 @@ #include "nsEditor.h" #include "nsIDOMNodeList.h" #include "nsIDOMCharacterData.h" -#include "nsIDOMSelection.h" #ifdef NS_DEBUG static PRBool gNoisy = PR_FALSE; @@ -39,12 +38,12 @@ JoinElementTxn::JoinElementTxn() /* log description initialized in parent constructor */ } -NS_IMETHODIMP JoinElementTxn::Init(nsIEditor *aEditor, +NS_IMETHODIMP JoinElementTxn::Init(nsEditor *aEditor, nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode) { - NS_PRECONDITION((aEditor && aLeftNode && aRightNode), "null arg"); - if (!aEditor || !aLeftNode || !aRightNode) { return NS_ERROR_NULL_POINTER; } + NS_PRECONDITION((aEditor && aLeftNode && aRightNode), "null arg"); + if (!aEditor || !aLeftNode || !aRightNode) { return NS_ERROR_NULL_POINTER; } mEditor = aEditor; mLeftNode = do_QueryInterface(aLeftNode); mRightNode = do_QueryInterface(aRightNode); @@ -60,20 +59,20 @@ JoinElementTxn::~JoinElementTxn() NS_IMETHODIMP JoinElementTxn::Do(void) { if (gNoisy) { printf("%p Do Join of %p and %p\n", this, mLeftNode.get(), mRightNode.get()); } - NS_PRECONDITION((mEditor && mLeftNode && mRightNode), "null arg"); - if (!mEditor || !mLeftNode || !mRightNode) { return NS_ERROR_NOT_INITIALIZED; } + NS_PRECONDITION((mEditor && mLeftNode && mRightNode), "null arg"); + if (!mEditor || !mLeftNode || !mRightNode) { return NS_ERROR_NOT_INITIALIZED; } // get the parent node nsCOMPtrleftParent; nsresult result = mLeftNode->GetParentNode(getter_AddRefs(leftParent)); - if (NS_FAILED(result)) return result; - if (!leftParent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!leftParent) return NS_ERROR_NULL_POINTER; // verify that mLeftNode and mRightNode have the same parent nsCOMPtrrightParent; result = mRightNode->GetParentNode(getter_AddRefs(rightParent)); - if (NS_FAILED(result)) return result; - if (!rightParent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!rightParent) return NS_ERROR_NULL_POINTER; if (leftParent==rightParent) { @@ -89,21 +88,16 @@ NS_IMETHODIMP JoinElementTxn::Do(void) { nsCOMPtr childNodes; result = mLeftNode->GetChildNodes(getter_AddRefs(childNodes)); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) return result; if (childNodes) { childNodes->GetLength(&mOffset); } } - result = nsEditor::JoinNodesImpl(mRightNode, mLeftNode, mParent, PR_FALSE); + result = mEditor->JoinNodesImpl(mRightNode, mLeftNode, mParent, PR_FALSE); if (NS_SUCCEEDED(result)) { if (gNoisy) { printf(" left node = %p removed\n", mLeftNode.get()); } - nsCOMPtrselection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - selection->Collapse(mRightNode, mOffset); } } else diff --git a/mozilla/editor/libeditor/base/JoinElementTxn.h b/mozilla/editor/libeditor/base/JoinElementTxn.h index d6af6af6988..8550de2e7a2 100644 --- a/mozilla/editor/libeditor/base/JoinElementTxn.h +++ b/mozilla/editor/libeditor/base/JoinElementTxn.h @@ -33,6 +33,7 @@ 0x9bc5f9f0, 0xac48, 0x11d2, \ {0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } +class nsEditor; /** * A transaction that joins two elements E1 (left node) and E2 (right node) @@ -51,7 +52,7 @@ public: * @param aLeftNode the first of two nodes to join * @param aRightNode the second of two nodes to join */ - NS_IMETHOD Init(nsIEditor *aEditor, + NS_IMETHOD Init(nsEditor *aEditor, nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode); protected: @@ -95,7 +96,7 @@ protected: /** the parent node containing mLeftNode and mRightNode */ nsCOMPtr mParent; - nsIEditor* mEditor; + nsEditor* mEditor; friend class TransactionFactory; diff --git a/mozilla/editor/libeditor/base/SplitElementTxn.cpp b/mozilla/editor/libeditor/base/SplitElementTxn.cpp index 7224a709182..513f8847f0a 100644 --- a/mozilla/editor/libeditor/base/SplitElementTxn.cpp +++ b/mozilla/editor/libeditor/base/SplitElementTxn.cpp @@ -41,12 +41,12 @@ SplitElementTxn::SplitElementTxn() /* log description initialized in parent constructor */ } -NS_IMETHODIMP SplitElementTxn::Init(nsIEditor *aEditor, +NS_IMETHODIMP SplitElementTxn::Init(nsEditor *aEditor, nsIDOMNode *aNode, PRInt32 aOffset) { - NS_ASSERTION(aEditor && aNode, "bad args"); - if (!aEditor || !aNode) { return NS_ERROR_NOT_INITIALIZED; } + NS_ASSERTION(aEditor && aNode, "bad args"); + if (!aEditor || !aNode) { return NS_ERROR_NOT_INITIALIZED; } mEditor = aEditor; mExistingRightNode = do_QueryInterface(aNode); @@ -67,17 +67,17 @@ NS_IMETHODIMP SplitElementTxn::Do(void) // create a new node nsresult result = mExistingRightNode->CloneNode(PR_FALSE, getter_AddRefs(mNewLeftNode)); NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element."); - if (NS_FAILED(result)) return result; - if (!mNewLeftNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!mNewLeftNode) return NS_ERROR_NULL_POINTER; if (gNoisy) { printf(" created left node = %p\n", mNewLeftNode.get()); } // get the parent node result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent)); - if (NS_FAILED(result)) return result; - if (!mParent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!mParent) return NS_ERROR_NULL_POINTER; // insert the new node - result = nsEditor::SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); + result = mEditor->SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); if (NS_SUCCEEDED(result) && mNewLeftNode) { // Insert formatting whitespace for the new node: @@ -85,8 +85,8 @@ NS_IMETHODIMP SplitElementTxn::Do(void) nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; result = selection->Collapse(mNewLeftNode, mOffset); } else { @@ -107,8 +107,7 @@ NS_IMETHODIMP SplitElementTxn::Undo(void) } // this assumes Do inserted the new node in front of the prior existing node - nsresult result; - result = nsEditor::JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); + nsresult result = mEditor->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); if (gNoisy) { printf("** after join left child node %p into right node %p\n", mNewLeftNode.get(), mExistingRightNode.get()); @@ -117,14 +116,6 @@ NS_IMETHODIMP SplitElementTxn::Undo(void) if (NS_SUCCEEDED(result)) { if (gNoisy) { printf(" left node = %p removed\n", mNewLeftNode.get()); } - nsCOMPtrselection; - mEditor->GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - result = selection->Collapse(mExistingRightNode, mOffset); - } - else { - result = NS_ERROR_NOT_IMPLEMENTED; } return result; } diff --git a/mozilla/editor/libeditor/base/SplitElementTxn.h b/mozilla/editor/libeditor/base/SplitElementTxn.h index 6ac3f9b3073..0e03c5831d7 100644 --- a/mozilla/editor/libeditor/base/SplitElementTxn.h +++ b/mozilla/editor/libeditor/base/SplitElementTxn.h @@ -33,6 +33,8 @@ 0x690c6290, 0xac48, 0x11d2, \ {0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} } +class nsEditor; + /** * A transaction that splits an element E into two identical nodes, E1 and E2 * with the children of E divided between E1 and E2. @@ -50,7 +52,7 @@ public: * aOffset may refer to children of aNode, or content of aNode. * The left node will have child|content 0..aOffset-1. */ - NS_IMETHOD Init (nsIEditor *aEditor, + NS_IMETHOD Init (nsEditor *aEditor, nsIDOMNode *aNode, PRInt32 aOffset); protected: @@ -93,7 +95,7 @@ protected: /** the parent shared by mExistingRightNode and mNewLeftNode */ nsCOMPtr mParent; - nsIEditor* mEditor; + nsEditor* mEditor; friend class TransactionFactory; diff --git a/mozilla/editor/libeditor/base/nsEditor.cpp b/mozilla/editor/libeditor/base/nsEditor.cpp index 2ee52d1f27f..90844187392 100644 --- a/mozilla/editor/libeditor/base/nsEditor.cpp +++ b/mozilla/editor/libeditor/base/nsEditor.cpp @@ -632,6 +632,14 @@ nsEditor::EndPlaceHolderTransaction() return NS_OK; } +NS_IMETHODIMP nsEditor::ShouldTxnSetSelection(PRBool *aResult) +{ + if (!aResult) return NS_ERROR_NULL_POINTER; + *aResult = mShouldTxnSetSelection; + return NS_OK; +} + + // XXX: the rule system should tell us which node to select all on (ie, the root, or the body) NS_IMETHODIMP nsEditor::SelectAll() { @@ -1743,24 +1751,24 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) if (NS_SUCCEEDED(result)) { - EditTxn *txn; - if (mInIMEMode) - { - if (!mIMETextNode) - { + EditTxn *txn; + if (mInIMEMode) + { + if (!mIMETextNode) + { mIMETextNode = nodeAsText; mIMETextOffset = offset; } - result = CreateTxnForIMEText(aStringToInsert,(IMETextTxn**)&txn); - } - else - { - result = CreateTxnForInsertText(aStringToInsert, nodeAsText, offset, (InsertTextTxn**)&txn); - } - if (NS_FAILED(result)) return result; - if (!txn) return NS_ERROR_OUT_OF_MEMORY; - - // let listeners know whats up + result = CreateTxnForIMEText(aStringToInsert,(IMETextTxn**)&txn); + } + else + { + result = CreateTxnForInsertText(aStringToInsert, nodeAsText, offset, (InsertTextTxn**)&txn); + } + if (NS_FAILED(result)) return result; + if (!txn) return NS_ERROR_OUT_OF_MEMORY; + + // let listeners know whats up PRInt32 i; nsIEditActionListener *listener; if (mActionListeners) @@ -1780,7 +1788,7 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) NS_IF_RELEASE(txn); EndUpdateViewBatch(); - // let listeners know what happened + // let listeners know what happened if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -2024,10 +2032,11 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert, result = TransactionFactory::GetNewTransaction(InsertTextTxn::GetCID(), (EditTxn **)aTxn); if (NS_FAILED(result)) return result; if (!*aTxn) return NS_ERROR_OUT_OF_MEMORY; - result = (*aTxn)->Init(aTextNode, aOffset, aStringToInsert, mPresShellWeak); + result = (*aTxn)->Init(aTextNode, aOffset, aStringToInsert, this); return result; } + NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aLength) @@ -2037,7 +2046,7 @@ NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, nsAutoRules beginRulesSniffing(this, kOpDeleteText, nsIEditor::ePrevious); if (NS_SUCCEEDED(result)) { - // let listeners know whats up + // let listeners know whats up PRInt32 i; nsIEditActionListener *listener; if (mActionListeners) @@ -2052,7 +2061,7 @@ NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, result = Do(txn); - // let listeners know what happened + // let listeners know what happened if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -2127,7 +2136,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, #pragma mark - #endif -// BEGIN nsEditor public static helper methods +// BEGIN nsEditor public helper methods nsresult nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, @@ -2147,6 +2156,20 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, (nsnull!=aNewLeftNode) && (nsnull!=aParent)) { + // get selection + nsCOMPtr selection; + GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + // remember some selection points + nsCOMPtr selStartNode, selEndNode; + PRInt32 selStartOffset, selEndOffset; + result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset); + if (NS_FAILED(result)) return result; + result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset); + if (NS_FAILED(result)) return result; + nsCOMPtr resultNode; result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode)); //printf(" after insert\n"); content->List(); // DEBUG @@ -2197,6 +2220,41 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, } } } + // handle selection + if (GetShouldTxnSetSelection()) + { + // editor wants us to set selection at split point + selection->Collapse(aNewLeftNode, aOffset); + } + else + { + // and adjust the selection if needed + // HACK: this is overly simplified - multi-range selections need more work than this + if (selStartNode == aExistingRightNode) + { + if (selStartOffset < aOffset) + { + selStartNode = aNewLeftNode; + } + else + { + selStartOffset -= aOffset; + } + } + if (selEndNode == aExistingRightNode) + { + if (selEndOffset < aOffset) + { + selEndNode = aNewLeftNode; + } + else + { + selEndOffset -= aOffset; + } + } + selection->Collapse(selStartNode,selStartOffset); + selection->Extend(selEndNode,selEndOffset); + } } } } @@ -2213,14 +2271,28 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, PRBool aNodeToKeepIsFirst) { nsresult result = NS_OK; - NS_ASSERTION(((nsnull!=aNodeToKeep) && - (nsnull!=aNodeToJoin) && - (nsnull!=aParent)), - "null arg"); - if ((nsnull!=aNodeToKeep) && - (nsnull!=aNodeToJoin) && - (nsnull!=aParent)) + NS_ASSERTION(aNodeToKeep && aNodeToJoin && aParent, "null arg"); + if (aNodeToKeep && aNodeToJoin && aParent) { + // get selection + nsCOMPtr selection; + GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + // remember some selection points + nsCOMPtr selStartNode, selEndNode; + PRInt32 selStartOffset, selEndOffset; + result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset); + if (NS_FAILED(result)) return result; + result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset); + if (NS_FAILED(result)) return result; + PRUint32 firstNodeLength; + nsCOMPtr leftNode = aNodeToJoin; + if (aNodeToKeepIsFirst) leftNode = aNodeToKeep; + result = GetLengthOfDOMNode(leftNode, firstNodeLength); + if (NS_FAILED(result)) return result; + // if it's a text node, just shuffle around some text nsCOMPtr keepNodeAsText( do_QueryInterface(aNodeToKeep) ); nsCOMPtr joinNodeAsText( do_QueryInterface(aNodeToJoin) ); @@ -2249,9 +2321,9 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, { PRInt32 i; // must be signed int! PRUint32 childCount=0; - childNodes->GetLength(&childCount); nsCOMPtr firstNode; //only used if aNodeToKeepIsFirst is false - if (PR_FALSE==aNodeToKeepIsFirst) + childNodes->GetLength(&childCount); + if (!aNodeToKeepIsFirst) { // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it result = aNodeToKeep->GetFirstChild(getter_AddRefs(firstNode)); // GetFirstChild returns nsnull firstNode if aNodeToKeep has no children, that's ok. @@ -2265,7 +2337,7 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, result = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(result)) && (childNode)) { - if (PR_TRUE==aNodeToKeepIsFirst) + if (aNodeToKeepIsFirst) { // append children of aNodeToJoin //was result = aNodeToKeep->AppendChild(childNode, getter_AddRefs(resultNode)); result = aNodeToKeep->InsertBefore(childNode, previousChild, getter_AddRefs(resultNode)); @@ -2288,6 +2360,35 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, { // delete the extra node nsCOMPtr resultNode; result = aParent->RemoveChild(aNodeToJoin, getter_AddRefs(resultNode)); + + if (GetShouldTxnSetSelection()) + { + // editor wants us to set selection at join point + selection->Collapse(aNodeToKeep, firstNodeLength); + } + else + { + // and adjust the selection if needed + // HACK: this is overly simplified - multi-range selections need more work than this + if (selStartNode == aNodeToJoin) + { + selStartNode = aNodeToKeep; + if (aNodeToKeepIsFirst) + { + selStartOffset += firstNodeLength; + } + } + if (selEndNode == aNodeToJoin) + { + selEndNode = aNodeToKeep; + if (aNodeToKeepIsFirst) + { + selEndOffset += firstNodeLength; + } + } + selection->Collapse(selStartNode,selStartOffset); + selection->Extend(selEndNode,selEndOffset); + } } } else @@ -3998,13 +4099,13 @@ nsresult nsEditor::EndUpdateViewBatch() { NS_PRECONDITION(mUpdateCount>0, "bad state"); - nsCOMPtr presShell; + nsCOMPtr presShell; nsresult rv = GetPresShell(getter_AddRefs(presShell)); if (NS_FAILED(rv)) return rv; StCaretHider caretHider(presShell); - + nsCOMPtrselection; nsresult selectionResult = GetSelection(getter_AddRefs(selection)); if (NS_SUCCEEDED(selectionResult) && selection) { @@ -4028,6 +4129,18 @@ nsresult nsEditor::EndUpdateViewBatch() return NS_OK; } +PRBool +nsEditor::GetShouldTxnSetSelection() +{ + return mShouldTxnSetSelection; +} + + +void +nsEditor::SetShouldTxnSetSelection(PRBool aShould) +{ + mShouldTxnSetSelection = aShould; +} #ifdef XP_MAC #pragma mark - @@ -4270,7 +4383,7 @@ nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert, { NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn"); if(!aTxn) return NS_ERROR_NULL_POINTER; - + nsresult result; result = TransactionFactory::GetNewTransaction(IMETextTxn::GetCID(), (EditTxn **)aTxn); diff --git a/mozilla/editor/libeditor/base/nsEditor.h b/mozilla/editor/libeditor/base/nsEditor.h index 344976c90c7..2241d5f4a3f 100644 --- a/mozilla/editor/libeditor/base/nsEditor.h +++ b/mozilla/editor/libeditor/base/nsEditor.h @@ -142,6 +142,7 @@ public: NS_IMETHOD BeginPlaceHolderTransaction(nsIAtom *aName); NS_IMETHOD EndPlaceHolderTransaction(); + NS_IMETHOD ShouldTxnSetSelection(PRBool *aResult); // pure virtual, because the definition of 'empty' depends on the doc type NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty)=0; @@ -396,10 +397,10 @@ public: * @param aNewLeftNode [OUT] the new node resulting from the split, becomes aExistingRightNode's previous sibling. * @param aParent the parent of aExistingRightNode */ - static nsresult SplitNodeImpl(nsIDOMNode *aExistingRightNode, - PRInt32 aOffset, - nsIDOMNode *aNewLeftNode, - nsIDOMNode *aParent); + nsresult SplitNodeImpl(nsIDOMNode *aExistingRightNode, + PRInt32 aOffset, + nsIDOMNode *aNewLeftNode, + nsIDOMNode *aParent); /** * JoinNodes() takes 2 nodes and merge their content|children. @@ -410,10 +411,10 @@ public: * @param aNodeToKeepIsFirst if PR_TRUE, the contents|children of aNodeToKeep come before the * contents|children of aNodeToJoin, otherwise their positions are switched. */ - static nsresult JoinNodesImpl(nsIDOMNode *aNodeToKeep, - nsIDOMNode *aNodeToJoin, - nsIDOMNode *aParent, - PRBool aNodeToKeepIsFirst); + nsresult JoinNodesImpl(nsIDOMNode *aNodeToKeep, + nsIDOMNode *aNodeToJoin, + nsIDOMNode *aParent, + PRBool aNodeToKeepIsFirst); /** * Set aOffset to the offset of aChild in aParent. @@ -629,6 +630,8 @@ public: nsresult BeginUpdateViewBatch(void); nsresult EndUpdateViewBatch(void); + PRBool GetShouldTxnSetSelection(); + void SetShouldTxnSetSelection(PRBool aShould); protected: @@ -645,7 +648,7 @@ protected: PRInt32 mPlaceHolderBatch; // nesting count for batching nsCOMPtr mTxnStartNode; // saved selection info to pass to placeholder at init time PRInt32 mTxnStartOffset; // " " " " - + PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns // // data necessary to build IME transactions // @@ -666,6 +669,7 @@ protected: static PRInt32 gInstanceCount; friend PRBool NSCanUnload(nsISupports* serviceMgr); + friend class nsAutoTxnsConserveSelection; }; diff --git a/mozilla/editor/libeditor/base/nsEditorUtils.h b/mozilla/editor/libeditor/base/nsEditorUtils.h index e70472714a8..be12960f5b3 100644 --- a/mozilla/editor/libeditor/base/nsEditorUtils.h +++ b/mozilla/editor/libeditor/base/nsEditorUtils.h @@ -107,5 +107,36 @@ class nsAutoRules }; +/*************************************************************************** + * stack based helper class for turning off active selection adjustment + * by low level transactions + */ +class nsAutoTxnsConserveSelection +{ + public: + + nsAutoTxnsConserveSelection(nsEditor *ed) : mEd(ed), mOldState(PR_TRUE) + { + if (mEd) + { + mOldState = mEd->GetShouldTxnSetSelection(); + mEd->SetShouldTxnSetSelection(PR_FALSE); + } + } + + ~nsAutoTxnsConserveSelection() + { + if (mEd) + { + mEd->SetShouldTxnSetSelection(mOldState); + } + } + + protected: + nsEditor *mEd; + PRBool mOldState; +}; + + #endif // nsEditorUtils_h__ diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp index e01f1a5a353..876ff6eddb3 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp @@ -153,6 +153,8 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) { + nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); + // expand the "changed doc range" as needed res = PromoteRange(mDocChangeRange, action); if (NS_FAILED(res)) return res; @@ -2687,7 +2689,7 @@ nsHTMLEditRules::ReplaceContainer(nsIDOMNode *inNode, // the parent of inNode // nsresult -nsHTMLEditRules::RemoveContainer(nsIDOMNode *inNode) +nsHTMLEditRules::RemoveContainer(nsIDOMNode *inNode, PRBool aAddBRIfNeeded) { if (!inNode) return NS_ERROR_NULL_POINTER; @@ -2698,6 +2700,38 @@ nsHTMLEditRules::RemoveContainer(nsIDOMNode *inNode) PRInt32 offset; res = nsEditor::GetNodeLocation(inNode, &parent, &offset); if (NS_FAILED(res)) return res; + + // add BR's before and/or after the inNode + // if aAddBRIfNeeded is set and if the content + // before/after the node is inline. + // Only do this if inNode is a block node. + if (aAddBRIfNeeded && mEditor->IsBlockNode(inNode)) + { + nsCOMPtr nearNode, brNode; + res = GetPriorHTMLSibling(inNode, &nearNode); + if (NS_FAILED(res)) return res; + if (nearNode && mEditor->IsInlineNode(nearNode)) + { + res = mEditor->CreateBR(parent, offset, &brNode); + if (NS_FAILED(res)) return res; + // refresh location info + res = nsEditor::GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + } + res = GetNextHTMLSibling(inNode, &nearNode); + if (NS_FAILED(res)) return res; + if (nearNode && mEditor->IsInlineNode(nearNode)) + { + res = mEditor->CreateBR(parent, offset+1, &brNode); + if (NS_FAILED(res)) return res; + // refresh location info + res = nsEditor::GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + } + } + + // loop through the child nodes of inNode and promote them + // into inNode's parent. PRBool bHasMoreChildren; inNode->HasChildNodes(&bHasMoreChildren); nsCOMPtr child; @@ -3249,7 +3283,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString // we special case an empty tag name to mean "remove block parents". // This is used for the "normal" paragraph style in mail-compose - if (aBlockTag->IsEmpty()) bNoParent = PR_TRUE; + if (aBlockTag->IsEmpty() || *aBlockTag=="normal") bNoParent = PR_TRUE; arrayOfNodes->Count(&listCount); @@ -3272,11 +3306,6 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString continue; // do nothing to this block } - // if curNode is a
 and we are converting to non-pre, we need
-    // to process the text inside the 
 so as to convert returns
-    // to breaks, and runs of spaces to nbsps.
-    // xxx floppy moose
-      
     // if curNode is a mozdiv, p, header, address, or pre, replace 
     // it with a new block of correct type.
     // xxx floppy moose: pre cant hold everything the others can
@@ -3294,10 +3323,10 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
       curBlock = 0;  // forget any previous block used for previous inline nodes
       if (bNoParent)
       {
-        nsCOMPtr brNode;
-        res = mEditor->CreateBR(curParent, offset+1, &brNode);
-        if (NS_FAILED(res)) return res;
-        res = RemoveContainer(curNode);
+//        nsCOMPtr brNode;
+//        res = mEditor->CreateBR(curParent, offset+1, &brNode);
+//        if (NS_FAILED(res)) return res;
+        res = RemoveContainer(curNode, PR_TRUE); 
       }
       else
       {
@@ -4029,10 +4058,22 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
   // unwrap list item contents if they are no longer in a list
   if (!IsList(curParPar) && IsListItem(curNode)) 
   {
-    nsCOMPtr mozDiv;
+    nsCOMPtr lastChild;
+    res = GetLastEditableChild(curNode, &lastChild);
+    if (NS_FAILED(res)) return res;
     res = RemoveContainer(curNode);
     if (NS_FAILED(res)) return res;
-
+    if (mEditor->IsInlineNode(lastChild))
+    {
+      // last thing inside of the listitem wasn't a block node.
+      // insert a BR to preserve the illusion of block boundaries
+      nsCOMPtr node, brNode;
+      PRInt32 offset;
+      res = nsEditor::GetNodeLocation(lastChild, &node, &offset);
+      if (NS_FAILED(res)) return res;
+      res = mEditor->CreateBR(node, offset+1, &brNode);
+      if (NS_FAILED(res)) return res;
+    }
     *aOutOfList = PR_TRUE;
   }
   return res;
diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.h b/mozilla/editor/libeditor/html/nsHTMLEditRules.h
index da8b675f810..51aac544b99 100644
--- a/mozilla/editor/libeditor/html/nsHTMLEditRules.h
+++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.h
@@ -133,7 +133,7 @@ protected:
   nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag);
 
   nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType);
-  nsresult RemoveContainer(nsIDOMNode *inNode);
+  nsresult RemoveContainer(nsIDOMNode *inNode, PRBool aAddBRIfNeeded=PR_FALSE);
   nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType);
 
   nsresult JoinNodesSmart( nsIDOMNode *aNodeLeft, 
diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp
index bf452fa81c2..aef7b9bb128 100644
--- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp
@@ -1818,13 +1818,9 @@ NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat)
   //Kinda sad to waste memory just to force lower case
   nsAutoString tag = aParagraphFormat;
   tag.ToLowerCase();
-  if (tag == "normal") 
-  {
-    res = InsertBasicBlock("p");
-  } 
   //XXX: TODO: Do we really want to support setting list types here?
   // If yes, we need to add "dd", "dt" for 
support - else if (tag == "li") + if (tag == "li") { //XXX: Why do we assume "ul" What about "ol"? This will change an OL into a UL list! res = MakeOrChangeList("ul"); diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.cpp b/mozilla/editor/libeditor/text/nsTextEditRules.cpp index 6b11e9fa013..00ee5a36829 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.cpp +++ b/mozilla/editor/libeditor/text/nsTextEditRules.cpp @@ -1080,9 +1080,6 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, res = mEditor->JoinNodes(selectedNode, siblingNode, parentNode); if (NS_FAILED(res)) return res; // selectedNode will remain after the join, siblingNode is removed - // set selection - res = aSelection->Collapse(siblingNode, selectedNodeLength); - if (NS_FAILED(res)) return res; } } // if, after all this work, selectedNode is empty, delete it diff --git a/mozilla/editor/public/nsIEditor.h b/mozilla/editor/public/nsIEditor.h index 889c23d9ff3..3ee9a2c443f 100644 --- a/mozilla/editor/public/nsIEditor.h +++ b/mozilla/editor/public/nsIEditor.h @@ -212,6 +212,7 @@ public: NS_IMETHOD BeginPlaceHolderTransaction(nsIAtom *aName)=0; NS_IMETHOD EndPlaceHolderTransaction()=0; + NS_IMETHOD ShouldTxnSetSelection(PRBool *aResult)=0; /* ------------ Clipboard methods -------------- */