michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include // for printf michael@0: michael@0: #include "SplitElementTxn.h" michael@0: #include "nsAString.h" michael@0: #include "nsDebug.h" // for NS_ASSERTION, etc michael@0: #include "nsEditor.h" // for nsEditor michael@0: #include "nsError.h" // for NS_ERROR_NOT_INITIALIZED, etc michael@0: #include "nsIDOMCharacterData.h" // for nsIDOMCharacterData michael@0: #include "nsIDOMNode.h" // for nsIDOMNode michael@0: #include "nsIEditor.h" // for nsEditor::DebugDumpContent, etc michael@0: #include "nsISelection.h" // for nsISelection michael@0: #include "nsISupportsUtils.h" // for NS_ADDREF michael@0: michael@0: // note that aEditor is not refcounted michael@0: SplitElementTxn::SplitElementTxn() michael@0: : EditTxn() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitElementTxn, EditTxn, michael@0: mParent, michael@0: mNewLeftNode) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(SplitElementTxn, EditTxn) michael@0: NS_IMPL_RELEASE_INHERITED(SplitElementTxn, EditTxn) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SplitElementTxn) michael@0: NS_INTERFACE_MAP_END_INHERITING(EditTxn) michael@0: michael@0: NS_IMETHODIMP SplitElementTxn::Init(nsEditor *aEditor, michael@0: nsIDOMNode *aNode, michael@0: int32_t aOffset) michael@0: { michael@0: NS_ASSERTION(aEditor && aNode, "bad args"); michael@0: if (!aEditor || !aNode) { return NS_ERROR_NOT_INITIALIZED; } michael@0: michael@0: mEditor = aEditor; michael@0: mExistingRightNode = do_QueryInterface(aNode); michael@0: mOffset = aOffset; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP SplitElementTxn::DoTransaction(void) michael@0: { michael@0: NS_ASSERTION(mExistingRightNode && mEditor, "bad state"); michael@0: if (!mExistingRightNode || !mEditor) { return NS_ERROR_NOT_INITIALIZED; } michael@0: michael@0: // create a new node michael@0: nsresult result = mExistingRightNode->CloneNode(false, 1, getter_AddRefs(mNewLeftNode)); michael@0: NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element."); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: NS_ENSURE_TRUE(mNewLeftNode, NS_ERROR_NULL_POINTER); michael@0: mEditor->MarkNodeDirty(mExistingRightNode); michael@0: michael@0: // get the parent node michael@0: result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent)); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: NS_ENSURE_TRUE(mParent, NS_ERROR_NULL_POINTER); michael@0: michael@0: // insert the new node michael@0: result = mEditor->SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); michael@0: if (mNewLeftNode) { michael@0: bool bAdjustSelection; michael@0: mEditor->ShouldTxnSetSelection(&bAdjustSelection); michael@0: if (bAdjustSelection) michael@0: { michael@0: nsCOMPtr selection; michael@0: result = mEditor->GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); michael@0: result = selection->Collapse(mNewLeftNode, mOffset); michael@0: } michael@0: else michael@0: { michael@0: // do nothing - dom range gravity will adjust selection michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: NS_IMETHODIMP SplitElementTxn::UndoTransaction(void) michael@0: { michael@0: NS_ASSERTION(mEditor && mExistingRightNode && mNewLeftNode && mParent, "bad state"); michael@0: if (!mEditor || !mExistingRightNode || !mNewLeftNode || !mParent) { michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: // this assumes Do inserted the new node in front of the prior existing node michael@0: nsCOMPtr rightNode = do_QueryInterface(mExistingRightNode); michael@0: nsCOMPtr leftNode = do_QueryInterface(mNewLeftNode); michael@0: nsCOMPtr parent = do_QueryInterface(mParent); michael@0: NS_ENSURE_TRUE(rightNode && leftNode && parent, NS_ERROR_FAILURE); michael@0: return mEditor->JoinNodesImpl(rightNode, leftNode, parent); michael@0: } michael@0: michael@0: /* redo cannot simply resplit the right node, because subsequent transactions michael@0: * on the redo stack may depend on the left node existing in its previous state. michael@0: */ michael@0: NS_IMETHODIMP SplitElementTxn::RedoTransaction(void) michael@0: { michael@0: NS_ASSERTION(mEditor && mExistingRightNode && mNewLeftNode && mParent, "bad state"); michael@0: if (!mEditor || !mExistingRightNode || !mNewLeftNode || !mParent) { michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: nsresult result; michael@0: nsCOMPtrresultNode; michael@0: // first, massage the existing node so it is in its post-split state michael@0: nsCOMPtrrightNodeAsText = do_QueryInterface(mExistingRightNode); michael@0: if (rightNodeAsText) michael@0: { michael@0: nsresult result = rightNodeAsText->DeleteData(0, mOffset); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: } michael@0: else michael@0: { michael@0: nsCOMPtrchild; michael@0: nsCOMPtrnextSibling; michael@0: result = mExistingRightNode->GetFirstChild(getter_AddRefs(child)); michael@0: int32_t i; michael@0: for (i=0; iGetNextSibling(getter_AddRefs(nextSibling)); michael@0: result = mExistingRightNode->RemoveChild(child, getter_AddRefs(resultNode)); michael@0: if (NS_SUCCEEDED(result)) michael@0: { michael@0: result = mNewLeftNode->AppendChild(child, getter_AddRefs(resultNode)); michael@0: } michael@0: child = do_QueryInterface(nextSibling); michael@0: } michael@0: } michael@0: // second, re-insert the left node into the tree michael@0: result = mParent->InsertBefore(mNewLeftNode, mExistingRightNode, getter_AddRefs(resultNode)); michael@0: return result; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP SplitElementTxn::GetTxnDescription(nsAString& aString) michael@0: { michael@0: aString.AssignLiteral("SplitElementTxn"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP SplitElementTxn::GetNewNode(nsIDOMNode **aNewNode) michael@0: { michael@0: NS_ENSURE_TRUE(aNewNode, NS_ERROR_NULL_POINTER); michael@0: NS_ENSURE_TRUE(mNewLeftNode, NS_ERROR_NOT_INITIALIZED); michael@0: *aNewNode = mNewLeftNode; michael@0: NS_ADDREF(*aNewNode); michael@0: return NS_OK; michael@0: }