2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsITransaction.h"
|
|
|
|
#include "nsTransactionStack.h"
|
|
|
|
#include "nsTransactionManager.h"
|
|
|
|
#include "nsTransactionItem.h"
|
|
|
|
#include "nsCOMPtr.h"
|
2008-12-10 08:46:36 -08:00
|
|
|
#include "nsAutoPtr.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsTransactionItem::nsTransactionItem(nsITransaction *aTransaction)
|
|
|
|
: mTransaction(aTransaction), mUndoStack(0), mRedoStack(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsTransactionItem::~nsTransactionItem()
|
|
|
|
{
|
|
|
|
if (mRedoStack)
|
|
|
|
delete mRedoStack;
|
|
|
|
|
|
|
|
if (mUndoStack)
|
|
|
|
delete mUndoStack;
|
2008-12-10 08:46:36 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-12-10 08:46:36 -08:00
|
|
|
nsrefcnt
|
|
|
|
nsTransactionItem::AddRef()
|
|
|
|
{
|
|
|
|
++mRefCnt;
|
|
|
|
NS_LOG_ADDREF(this, mRefCnt, "nsTransactionItem",
|
|
|
|
sizeof(nsTransactionItem));
|
|
|
|
return mRefCnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsrefcnt
|
|
|
|
nsTransactionItem::Release() {
|
|
|
|
--mRefCnt;
|
|
|
|
NS_LOG_RELEASE(this, mRefCnt, "nsTransactionItem");
|
|
|
|
if (mRefCnt == 0) {
|
|
|
|
mRefCnt = 1;
|
|
|
|
delete this;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return mRefCnt;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::AddChild(nsTransactionItem *aTransactionItem)
|
|
|
|
{
|
|
|
|
if (!aTransactionItem)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (!mUndoStack) {
|
|
|
|
mUndoStack = new nsTransactionStack();
|
|
|
|
if (!mUndoStack)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
mUndoStack->Push(aTransactionItem);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::GetTransaction(nsITransaction **aTransaction)
|
|
|
|
{
|
|
|
|
if (!aTransaction)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
2008-12-10 08:46:36 -08:00
|
|
|
NS_IF_ADDREF(*aTransaction = mTransaction);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::GetIsBatch(PRBool *aIsBatch)
|
|
|
|
{
|
|
|
|
if (!aIsBatch)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aIsBatch = !mTransaction;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::GetNumberOfChildren(PRInt32 *aNumChildren)
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
if (!aNumChildren)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aNumChildren = 0;
|
|
|
|
|
|
|
|
PRInt32 ui = 0;
|
|
|
|
PRInt32 ri = 0;
|
|
|
|
|
|
|
|
result = GetNumberOfUndoItems(&ui);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
result = GetNumberOfRedoItems(&ri);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
*aNumChildren = ui + ri;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::GetChild(PRInt32 aIndex, nsTransactionItem **aChild)
|
|
|
|
{
|
|
|
|
if (!aChild)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aChild = 0;
|
|
|
|
|
|
|
|
PRInt32 numItems = 0;
|
|
|
|
nsresult result = GetNumberOfChildren(&numItems);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (aIndex < 0 || aIndex >= numItems)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Children are expected to be in the order they were added,
|
|
|
|
// so the child first added would be at the bottom of the undo
|
|
|
|
// stack, or if there are no items on the undo stack, it would
|
|
|
|
// be at the top of the redo stack.
|
|
|
|
|
|
|
|
result = GetNumberOfUndoItems(&numItems);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (numItems > 0 && aIndex < numItems) {
|
|
|
|
if (!mUndoStack)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
return mUndoStack->GetItem(aIndex, aChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adjust the index for the redo stack:
|
|
|
|
|
|
|
|
aIndex -= numItems;
|
|
|
|
|
|
|
|
result = GetNumberOfRedoItems(&numItems);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (!mRedoStack || numItems == 0 || aIndex >= numItems)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
return mRedoStack->GetItem(numItems - aIndex - 1, aChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::DoTransaction()
|
|
|
|
{
|
|
|
|
if (mTransaction)
|
|
|
|
return mTransaction->DoTransaction();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr)
|
|
|
|
{
|
|
|
|
nsresult result = UndoChildren(aTxMgr);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
RecoverFromUndoError(aTxMgr);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mTransaction)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
result = mTransaction->UndoTransaction();
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
RecoverFromUndoError(aTxMgr);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
|
|
|
|
{
|
2008-12-10 08:46:36 -08:00
|
|
|
nsRefPtr<nsTransactionItem> item;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 sz = 0;
|
|
|
|
|
|
|
|
if (mUndoStack) {
|
|
|
|
if (!mRedoStack && mUndoStack) {
|
|
|
|
mRedoStack = new nsTransactionRedoStack();
|
|
|
|
if (!mRedoStack)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Undo all of the transaction items children! */
|
|
|
|
result = mUndoStack->GetSize(&sz);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
while (sz-- > 0) {
|
2008-12-10 08:46:36 -08:00
|
|
|
result = mUndoStack->Peek(getter_AddRefs(item));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-03-30 02:59:28 -07:00
|
|
|
if (NS_FAILED(result) || !item) {
|
2007-03-22 10:30:00 -07:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-12-10 08:46:36 -08:00
|
|
|
nsCOMPtr<nsITransaction> t;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-12-10 08:46:36 -08:00
|
|
|
result = item->GetTransaction(getter_AddRefs(t));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool doInterrupt = PR_FALSE;
|
|
|
|
|
|
|
|
result = aTxMgr->WillUndoNotify(t, &doInterrupt);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doInterrupt) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = item->UndoTransaction(aTxMgr);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
2008-12-10 08:46:36 -08:00
|
|
|
result = mUndoStack->Pop(getter_AddRefs(item));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = mRedoStack->Push(item);
|
|
|
|
|
|
|
|
/* XXX: If we got an error here, I doubt we can recover!
|
|
|
|
* XXX: Should we just push the item back on the undo stack?
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult result2 = aTxMgr->DidUndoNotify(t, result);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = result2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr)
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
|
2008-12-10 08:46:36 -08:00
|
|
|
nsCOMPtr<nsITransaction> kungfuDeathGrip(mTransaction);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mTransaction) {
|
|
|
|
result = mTransaction->RedoTransaction();
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = RedoChildren(aTxMgr);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
RecoverFromRedoError(aTxMgr);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
|
|
|
|
{
|
2008-12-10 08:46:36 -08:00
|
|
|
nsRefPtr<nsTransactionItem> item;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 sz = 0;
|
|
|
|
|
|
|
|
if (!mRedoStack)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
/* Redo all of the transaction items children! */
|
|
|
|
result = mRedoStack->GetSize(&sz);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
|
|
while (sz-- > 0) {
|
2008-12-10 08:46:36 -08:00
|
|
|
result = mRedoStack->Peek(getter_AddRefs(item));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-03-30 02:59:28 -07:00
|
|
|
if (NS_FAILED(result) || !item) {
|
2007-03-22 10:30:00 -07:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-12-10 08:46:36 -08:00
|
|
|
nsCOMPtr<nsITransaction> t;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-12-10 08:46:36 -08:00
|
|
|
result = item->GetTransaction(getter_AddRefs(t));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool doInterrupt = PR_FALSE;
|
|
|
|
|
|
|
|
result = aTxMgr->WillRedoNotify(t, &doInterrupt);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doInterrupt) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = item->RedoTransaction(aTxMgr);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
2008-12-10 08:46:36 -08:00
|
|
|
result = mRedoStack->Pop(getter_AddRefs(item));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = mUndoStack->Push(item);
|
|
|
|
|
|
|
|
// XXX: If we got an error here, I doubt we can recover!
|
|
|
|
// XXX: Should we just push the item back on the redo stack?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult result2 = aTxMgr->DidUndoNotify(t, result);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = result2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::GetNumberOfUndoItems(PRInt32 *aNumItems)
|
|
|
|
{
|
|
|
|
if (!aNumItems)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (!mUndoStack) {
|
|
|
|
*aNumItems = 0;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mUndoStack->GetSize(aNumItems);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::GetNumberOfRedoItems(PRInt32 *aNumItems)
|
|
|
|
{
|
|
|
|
if (!aNumItems)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (!mRedoStack) {
|
|
|
|
*aNumItems = 0;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mRedoStack->GetSize(aNumItems);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::RecoverFromUndoError(nsTransactionManager *aTxMgr)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// If this method gets called, we never got to the point where we
|
|
|
|
// successfully called UndoTransaction() for the transaction item itself.
|
|
|
|
// Just redo any children that successfully called undo!
|
|
|
|
//
|
|
|
|
return RedoChildren(aTxMgr);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionItem::RecoverFromRedoError(nsTransactionManager *aTxMgr)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// If this method gets called, we already successfully called
|
|
|
|
// RedoTransaction() for the transaction item itself. Undo all
|
|
|
|
// the children that successfully called RedoTransaction(),
|
|
|
|
// then undo the transaction item itself.
|
|
|
|
//
|
|
|
|
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
result = UndoChildren(aTxMgr);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mTransaction)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
return mTransaction->UndoTransaction();
|
|
|
|
}
|
|
|
|
|