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