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 "InsertTextTxn.h" michael@0: #include "nsAString.h" michael@0: #include "nsDebug.h" // for NS_ASSERTION, etc michael@0: #include "nsError.h" // for NS_OK, etc michael@0: #include "nsIDOMCharacterData.h" // for nsIDOMCharacterData michael@0: #include "nsIEditor.h" // for nsIEditor michael@0: #include "nsISelection.h" // for nsISelection michael@0: #include "nsISupportsUtils.h" // for NS_ADDREF_THIS, NS_RELEASE michael@0: #include "nsITransaction.h" // for nsITransaction michael@0: michael@0: InsertTextTxn::InsertTextTxn() michael@0: : EditTxn() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextTxn, EditTxn, michael@0: mElement) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(InsertTextTxn, EditTxn) michael@0: NS_IMPL_RELEASE_INHERITED(InsertTextTxn, EditTxn) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertTextTxn) michael@0: if (aIID.Equals(InsertTextTxn::GetCID())) { michael@0: *aInstancePtr = (void*)(InsertTextTxn*)this; michael@0: NS_ADDREF_THIS(); michael@0: return NS_OK; michael@0: } else michael@0: NS_INTERFACE_MAP_END_INHERITING(EditTxn) michael@0: michael@0: NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, michael@0: uint32_t aOffset, michael@0: const nsAString &aStringToInsert, michael@0: nsIEditor *aEditor) michael@0: { michael@0: #if 0 michael@0: nsAutoString text; michael@0: aElement->GetData(text); michael@0: printf("InsertTextTxn: Offset to insert at = %d. Text of the node to insert into:\n", aOffset); michael@0: wprintf(text.get()); michael@0: printf("\n"); michael@0: #endif michael@0: michael@0: NS_ASSERTION(aElement && aEditor, "bad args"); michael@0: NS_ENSURE_TRUE(aElement && aEditor, NS_ERROR_NULL_POINTER); michael@0: michael@0: mElement = do_QueryInterface(aElement); michael@0: mOffset = aOffset; michael@0: mStringToInsert = aStringToInsert; michael@0: mEditor = aEditor; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP InsertTextTxn::DoTransaction(void) michael@0: { michael@0: NS_ASSERTION(mElement && mEditor, "bad state"); michael@0: if (!mElement || !mEditor) { return NS_ERROR_NOT_INITIALIZED; } michael@0: michael@0: nsresult result = mElement->InsertData(mOffset, mStringToInsert); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: michael@0: // only set selection to insertion point if editor gives permission 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(mElement, mOffset+mStringToInsert.Length()); michael@0: NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); 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 InsertTextTxn::UndoTransaction(void) michael@0: { michael@0: NS_ASSERTION(mElement && mEditor, "bad state"); michael@0: if (!mElement || !mEditor) { return NS_ERROR_NOT_INITIALIZED; } michael@0: michael@0: uint32_t length = mStringToInsert.Length(); michael@0: return mElement->DeleteData(mOffset, length); michael@0: } michael@0: michael@0: NS_IMETHODIMP InsertTextTxn::Merge(nsITransaction *aTransaction, bool *aDidMerge) michael@0: { michael@0: // set out param default value michael@0: if (aDidMerge) michael@0: *aDidMerge = false; michael@0: nsresult result = NS_OK; michael@0: if (aDidMerge && aTransaction) michael@0: { michael@0: // if aTransaction is a InsertTextTxn, and if the selection hasn't changed, michael@0: // then absorb it michael@0: InsertTextTxn *otherInsTxn = nullptr; michael@0: aTransaction->QueryInterface(InsertTextTxn::GetCID(), (void **)&otherInsTxn); michael@0: if (otherInsTxn) michael@0: { michael@0: if (IsSequentialInsert(otherInsTxn)) michael@0: { michael@0: nsAutoString otherData; michael@0: otherInsTxn->GetData(otherData); michael@0: mStringToInsert += otherData; michael@0: *aDidMerge = true; michael@0: } michael@0: NS_RELEASE(otherInsTxn); michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: NS_IMETHODIMP InsertTextTxn::GetTxnDescription(nsAString& aString) michael@0: { michael@0: aString.AssignLiteral("InsertTextTxn: "); michael@0: aString += mStringToInsert; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* ============ protected methods ================== */ michael@0: michael@0: NS_IMETHODIMP InsertTextTxn::GetData(nsString& aResult) michael@0: { michael@0: aResult = mStringToInsert; michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool InsertTextTxn::IsSequentialInsert(InsertTextTxn *aOtherTxn) michael@0: { michael@0: NS_ASSERTION(aOtherTxn, "null param"); michael@0: if (aOtherTxn && aOtherTxn->mElement == mElement) michael@0: { michael@0: // here, we need to compare offsets. michael@0: int32_t length = mStringToInsert.Length(); michael@0: if (aOtherTxn->mOffset == (mOffset + length)) michael@0: return true; michael@0: } michael@0: return false; michael@0: }