2001-09-25 22:53:13 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
2004-04-18 14:21:17 +00:00
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
1998-11-19 21:43:21 +00:00
|
|
|
*
|
2004-04-18 14:21:17 +00:00
|
|
|
* 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/
|
1998-11-19 21:43:21 +00:00
|
|
|
*
|
2001-09-25 22:53:13 +00:00
|
|
|
* 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.
|
1998-11-19 21:43:21 +00:00
|
|
|
*
|
1999-11-06 03:43:54 +00:00
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
2004-04-18 14:21:17 +00:00
|
|
|
* The Initial Developer of the Original Code is
|
2001-09-25 22:53:13 +00:00
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
1999-11-06 03:43:54 +00:00
|
|
|
*
|
2001-09-25 22:53:13 +00:00
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
2004-04-18 14:21:17 +00:00
|
|
|
* 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"),
|
2001-09-25 22:53:13 +00:00
|
|
|
* 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
|
2004-04-18 14:21:17 +00:00
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
2001-09-25 22:53:13 +00:00
|
|
|
* 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
|
2004-04-18 14:21:17 +00:00
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
2001-09-25 22:53:13 +00:00
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
1998-11-19 21:43:21 +00:00
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
#include "nsITransaction.h"
|
|
|
|
#include "nsITransactionListener.h"
|
|
|
|
|
|
|
|
#include "nsTransactionItem.h"
|
|
|
|
#include "nsTransactionStack.h"
|
|
|
|
#include "nsVoidArray.h"
|
1998-11-19 21:58:58 +00:00
|
|
|
#include "nsTransactionManager.h"
|
2001-03-09 14:23:59 +00:00
|
|
|
#include "nsTransactionList.h"
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1998-12-17 19:26:17 +00:00
|
|
|
#include "nsCOMPtr.h"
|
1998-11-19 21:43:21 +00:00
|
|
|
|
1999-06-03 15:26:48 +00:00
|
|
|
#define LOCK_TX_MANAGER(mgr) (mgr)->Lock()
|
|
|
|
#define UNLOCK_TX_MANAGER(mgr) (mgr)->Unlock()
|
1998-11-24 00:45:21 +00:00
|
|
|
|
1998-11-20 18:04:57 +00:00
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
nsTransactionManager::nsTransactionManager(PRInt32 aMaxTransactionCount)
|
1999-05-26 21:16:25 +00:00
|
|
|
: mMaxTransactionCount(aMaxTransactionCount), mListeners(0)
|
1998-11-19 21:43:21 +00:00
|
|
|
{
|
1999-06-03 15:26:48 +00:00
|
|
|
mMonitor = ::PR_NewMonitor();
|
1998-11-19 21:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsTransactionManager::~nsTransactionManager()
|
|
|
|
{
|
1999-05-26 21:16:25 +00:00
|
|
|
if (mListeners)
|
|
|
|
{
|
|
|
|
PRInt32 i;
|
|
|
|
nsITransactionListener *listener;
|
|
|
|
|
|
|
|
for (i = 0; i < mListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
NS_IF_RELEASE(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete mListeners;
|
|
|
|
mListeners = 0;
|
|
|
|
}
|
1999-06-03 15:26:48 +00:00
|
|
|
|
|
|
|
if (mMonitor)
|
|
|
|
{
|
|
|
|
::PR_DestroyMonitor(mMonitor);
|
|
|
|
mMonitor = 0;
|
|
|
|
}
|
1998-11-19 21:43:21 +00:00
|
|
|
}
|
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
#ifdef DEBUG_TXMGR_REFCNT
|
|
|
|
|
|
|
|
nsrefcnt nsTransactionManager::AddRef(void)
|
|
|
|
{
|
|
|
|
return ++mRefCnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsrefcnt nsTransactionManager::Release(void)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
|
|
|
if (--mRefCnt == 0) {
|
|
|
|
NS_DELETEXPCOM(this);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return mRefCnt;
|
|
|
|
}
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
NS_IMPL_QUERY_INTERFACE2(nsTransactionManager, nsITransactionManager, nsISupportsWeakReference)
|
1999-11-22 21:03:04 +00:00
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
#else
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
NS_IMPL_ISUPPORTS2(nsTransactionManager, nsITransactionManager, nsISupportsWeakReference)
|
1998-11-19 21:43:21 +00:00
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
#endif
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
2001-03-09 14:23:59 +00:00
|
|
|
nsTransactionManager::DoTransaction(nsITransaction *aTransaction)
|
1998-11-19 21:43:21 +00:00
|
|
|
{
|
1999-02-08 17:28:33 +00:00
|
|
|
nsresult result;
|
1998-11-21 01:02:55 +00:00
|
|
|
|
|
|
|
if (!aTransaction)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1998-11-24 00:45:21 +00:00
|
|
|
LOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
PRBool doInterrupt = PR_FALSE;
|
|
|
|
|
|
|
|
result = WillDoNotify(aTransaction, &doInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (doInterrupt) {
|
1999-05-26 21:16:25 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-02-08 17:28:33 +00:00
|
|
|
result = BeginTransaction(aTransaction);
|
1998-12-01 18:38:52 +00:00
|
|
|
|
1999-02-08 17:28:33 +00:00
|
|
|
if (NS_FAILED(result)) {
|
1999-05-26 21:16:25 +00:00
|
|
|
DidDoNotify(aTransaction, result);
|
1998-11-24 00:45:21 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-02-08 17:28:33 +00:00
|
|
|
result = EndTransaction();
|
1998-11-21 01:02:55 +00:00
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
nsresult result2 = DidDoNotify(aTransaction, result);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = result2;
|
|
|
|
|
1998-11-24 00:45:21 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
|
|
|
return result;
|
1998-11-19 21:43:21 +00:00
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
2001-03-09 14:23:59 +00:00
|
|
|
nsTransactionManager::UndoTransaction()
|
1998-11-19 21:43:21 +00:00
|
|
|
{
|
1998-12-01 18:38:52 +00:00
|
|
|
nsresult result = NS_OK;
|
|
|
|
nsTransactionItem *tx = 0;
|
1998-11-21 01:02:55 +00:00
|
|
|
|
1998-11-24 00:45:21 +00:00
|
|
|
LOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
// It is illegal to call UndoTransaction() while the transaction manager is
|
|
|
|
// executing a transaction's DoTransaction() method! If this happens,
|
|
|
|
// the UndoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
|
1998-12-09 19:53:31 +00:00
|
|
|
|
|
|
|
result = mDoStack.Peek(&tx);
|
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
if (NS_FAILED(result)) {
|
1998-12-09 19:53:31 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
1998-11-21 01:02:55 +00:00
|
|
|
// Peek at the top of the undo stack. Don't remove the transaction
|
|
|
|
// until it has successfully completed.
|
1998-11-24 00:45:21 +00:00
|
|
|
result = mUndoStack.Peek(&tx);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
if (NS_FAILED(result)) {
|
1998-12-02 17:39:09 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1998-11-21 01:02:55 +00:00
|
|
|
// Bail if there's nothing on the stack.
|
1998-12-02 17:39:09 +00:00
|
|
|
if (!tx) {
|
1998-11-24 00:45:21 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
nsITransaction *t = 0;
|
|
|
|
|
|
|
|
result = tx->GetTransaction(&t);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
PRBool doInterrupt = PR_FALSE;
|
|
|
|
|
|
|
|
result = WillUndoNotify(t, &doInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (doInterrupt) {
|
1999-05-26 21:16:25 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
result = tx->UndoTransaction(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
1998-11-24 00:45:21 +00:00
|
|
|
result = mUndoStack.Pop(&tx);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = mRedoStack.Push(tx);
|
1998-11-21 01:02:55 +00:00
|
|
|
}
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
nsresult result2 = DidUndoNotify(t, result);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = result2;
|
|
|
|
|
1998-11-24 00:45:21 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
|
|
|
return result;
|
1998-11-19 21:43:21 +00:00
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
2001-03-09 14:23:59 +00:00
|
|
|
nsTransactionManager::RedoTransaction()
|
1998-11-19 21:43:21 +00:00
|
|
|
{
|
1998-12-01 18:38:52 +00:00
|
|
|
nsresult result = NS_OK;
|
|
|
|
nsTransactionItem *tx = 0;
|
1998-11-21 01:02:55 +00:00
|
|
|
|
1998-11-24 00:45:21 +00:00
|
|
|
LOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
// It is illegal to call RedoTransaction() while the transaction manager is
|
|
|
|
// executing a transaction's DoTransaction() method! If this happens,
|
|
|
|
// the RedoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
|
1998-12-09 19:53:31 +00:00
|
|
|
|
|
|
|
result = mDoStack.Peek(&tx);
|
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
if (NS_FAILED(result)) {
|
1998-12-09 19:53:31 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
1998-11-21 01:02:55 +00:00
|
|
|
// Peek at the top of the redo stack. Don't remove the transaction
|
|
|
|
// until it has successfully completed.
|
1998-11-24 00:45:21 +00:00
|
|
|
result = mRedoStack.Peek(&tx);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
if (NS_FAILED(result)) {
|
1998-12-02 17:39:09 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1998-11-21 01:02:55 +00:00
|
|
|
// Bail if there's nothing on the stack.
|
1998-12-02 17:39:09 +00:00
|
|
|
if (!tx) {
|
1998-11-24 00:45:21 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
nsITransaction *t = 0;
|
|
|
|
|
|
|
|
result = tx->GetTransaction(&t);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
PRBool doInterrupt = PR_FALSE;
|
|
|
|
|
|
|
|
result = WillRedoNotify(t, &doInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (doInterrupt) {
|
1999-05-26 21:16:25 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
result = tx->RedoTransaction(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
1998-11-24 00:45:21 +00:00
|
|
|
result = mRedoStack.Pop(&tx);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = mUndoStack.Push(tx);
|
1998-11-21 01:02:55 +00:00
|
|
|
}
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
nsresult result2 = DidRedoNotify(t, result);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = result2;
|
|
|
|
|
1998-11-24 00:45:21 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
1998-11-21 01:02:55 +00:00
|
|
|
|
|
|
|
return result;
|
1998-11-19 21:43:21 +00:00
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1998-12-02 17:39:09 +00:00
|
|
|
nsTransactionManager::Clear()
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
result = ClearRedoStack();
|
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
if (NS_FAILED(result)) {
|
1998-12-02 17:39:09 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = ClearUndoStack();
|
|
|
|
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1999-02-08 17:28:33 +00:00
|
|
|
nsTransactionManager::BeginBatch()
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
// We can batch independent transactions together by simply pushing
|
|
|
|
// a dummy transaction item on the do stack. This dummy transaction item
|
|
|
|
// will be popped off the do stack, and then pushed on the undo stack
|
|
|
|
// in EndBatch().
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
PRBool doInterrupt = PR_FALSE;
|
|
|
|
|
|
|
|
result = WillBeginBatchNotify(&doInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (doInterrupt) {
|
1999-05-26 21:16:25 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-02-08 17:28:33 +00:00
|
|
|
result = BeginTransaction(0);
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
nsresult result2 = DidBeginBatchNotify(result);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = result2;
|
|
|
|
|
1999-02-08 17:28:33 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1999-02-08 17:28:33 +00:00
|
|
|
nsTransactionManager::EndBatch()
|
|
|
|
{
|
|
|
|
nsTransactionItem *tx = 0;
|
|
|
|
nsITransaction *ti = 0;
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
// XXX: Need to add some mechanism to detect the case where the transaction
|
|
|
|
// at the top of the do stack isn't the dummy transaction, so we can
|
|
|
|
// throw an error!! This can happen if someone calls EndBatch() within
|
2001-03-09 14:23:59 +00:00
|
|
|
// the DoTransaction() method of a transaction.
|
1999-02-08 17:28:33 +00:00
|
|
|
//
|
|
|
|
// For now, we can detect this case by checking the value of the
|
|
|
|
// dummy transaction's mTransaction field. If it is our dummy
|
|
|
|
// transaction, it should be NULL. This may not be true in the
|
|
|
|
// future when we allow users to execute a transaction when beginning
|
|
|
|
// a batch!!!!
|
|
|
|
|
|
|
|
result = mDoStack.Peek(&tx);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx)
|
|
|
|
tx->GetTransaction(&ti);
|
|
|
|
|
|
|
|
if (!tx || ti) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
PRBool doInterrupt = PR_FALSE;
|
|
|
|
|
|
|
|
result = WillEndBatchNotify(&doInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (doInterrupt) {
|
1999-05-26 21:16:25 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-02-08 17:28:33 +00:00
|
|
|
result = EndTransaction();
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
nsresult result2 = DidEndBatchNotify(result);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = result2;
|
|
|
|
|
1999-02-08 17:28:33 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1998-11-20 00:43:07 +00:00
|
|
|
nsTransactionManager::GetNumberOfUndoItems(PRInt32 *aNumItems)
|
|
|
|
{
|
1998-12-02 17:39:09 +00:00
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
result = mUndoStack.GetSize(aNumItems);
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return result;
|
1998-11-20 00:43:07 +00:00
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1998-11-20 00:43:07 +00:00
|
|
|
nsTransactionManager::GetNumberOfRedoItems(PRInt32 *aNumItems)
|
|
|
|
{
|
1998-12-02 17:39:09 +00:00
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
result = mRedoStack.GetSize(aNumItems);
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsTransactionManager::GetMaxTransactionCount(PRInt32 *aMaxCount)
|
|
|
|
{
|
|
|
|
if (!aMaxCount)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
*aMaxCount = mMaxTransactionCount;
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1998-12-11 00:22:34 +00:00
|
|
|
nsTransactionManager::SetMaxTransactionCount(PRInt32 aMaxCount)
|
|
|
|
{
|
|
|
|
PRInt32 numUndoItems = 0, numRedoItems = 0, total = 0;
|
|
|
|
nsTransactionItem *tx = 0;
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
|
2002-02-20 23:22:31 +00:00
|
|
|
// It is illegal to call SetMaxTransactionCount() while the transaction
|
|
|
|
// manager is executing a transaction's DoTransaction() method because
|
|
|
|
// the undo and redo stacks might get pruned! If this happens, the
|
|
|
|
// SetMaxTransactionCount() request is ignored, and we return
|
|
|
|
// NS_ERROR_FAILURE.
|
1998-12-11 00:22:34 +00:00
|
|
|
|
2002-02-20 23:22:31 +00:00
|
|
|
result = mDoStack.Peek(&tx);
|
1998-12-11 00:22:34 +00:00
|
|
|
|
2002-02-20 23:22:31 +00:00
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
2002-02-20 03:08:32 +00:00
|
|
|
|
2002-02-20 23:22:31 +00:00
|
|
|
if (tx) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_ERROR_FAILURE;
|
1998-12-11 00:22:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If aMaxCount is less than zero, the user wants unlimited
|
|
|
|
// levels of undo! No need to prune the undo or redo stacks!
|
|
|
|
|
|
|
|
if (aMaxCount < 0) {
|
|
|
|
mMaxTransactionCount = -1;
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = mUndoStack.GetSize(&numUndoItems);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = mRedoStack.GetSize(&numRedoItems);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
total = numUndoItems + numRedoItems;
|
|
|
|
|
|
|
|
// If aMaxCount is greater than the number of transactions that currently
|
|
|
|
// exist on the undo and redo stack, there is no need to prune the
|
|
|
|
// undo or redo stacks!
|
|
|
|
|
|
|
|
if (aMaxCount > total ) {
|
|
|
|
mMaxTransactionCount = aMaxCount;
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try getting rid of some transactions on the undo stack! Start at
|
|
|
|
// the bottom of the stack and pop towards the top.
|
|
|
|
|
|
|
|
while (numUndoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
|
|
|
|
tx = 0;
|
|
|
|
result = mUndoStack.PopBottom(&tx);
|
|
|
|
|
|
|
|
if (NS_FAILED(result) || !tx) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete tx;
|
|
|
|
|
|
|
|
--numUndoItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If neccessary, get rid of some transactions on the redo stack! Start at
|
|
|
|
// the bottom of the stack and pop towards the top.
|
|
|
|
|
|
|
|
while (numRedoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) {
|
|
|
|
tx = 0;
|
|
|
|
result = mRedoStack.PopBottom(&tx);
|
|
|
|
|
|
|
|
if (NS_FAILED(result) || !tx) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete tx;
|
|
|
|
|
|
|
|
--numRedoItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
mMaxTransactionCount = aMaxCount;
|
|
|
|
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1998-12-02 17:39:09 +00:00
|
|
|
nsTransactionManager::PeekUndoStack(nsITransaction **aTransaction)
|
|
|
|
{
|
|
|
|
nsTransactionItem *tx = 0;
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
if (!aTransaction)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aTransaction = 0;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
result = mUndoStack.Peek(&tx);
|
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
if (NS_FAILED(result) || !tx) {
|
1998-12-02 17:39:09 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = tx->GetTransaction(aTransaction);
|
|
|
|
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
NS_IF_ADDREF(*aTransaction);
|
|
|
|
|
1998-12-02 17:39:09 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1998-12-02 17:39:09 +00:00
|
|
|
nsTransactionManager::PeekRedoStack(nsITransaction **aTransaction)
|
|
|
|
{
|
|
|
|
nsTransactionItem *tx = 0;
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
if (!aTransaction)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aTransaction = 0;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
result = mRedoStack.Peek(&tx);
|
|
|
|
|
1998-12-11 00:22:34 +00:00
|
|
|
if (NS_FAILED(result) || !tx) {
|
1998-12-02 17:39:09 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = tx->GetTransaction(aTransaction);
|
|
|
|
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
NS_IF_ADDREF(*aTransaction);
|
|
|
|
|
1998-12-02 17:39:09 +00:00
|
|
|
return result;
|
1998-11-20 00:43:07 +00:00
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
2001-03-09 14:23:59 +00:00
|
|
|
nsTransactionManager::GetUndoList(nsITransactionList **aTransactionList)
|
1998-11-20 00:43:07 +00:00
|
|
|
{
|
2001-03-09 14:23:59 +00:00
|
|
|
if (!aTransactionList)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aTransactionList = (nsITransactionList *)new nsTransactionList(this, &mUndoStack);
|
|
|
|
|
|
|
|
NS_IF_ADDREF(*aTransactionList);
|
1998-11-24 00:45:21 +00:00
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
return (! *aTransactionList) ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsTransactionManager::GetRedoList(nsITransactionList **aTransactionList)
|
|
|
|
{
|
|
|
|
if (!aTransactionList)
|
1998-11-24 00:45:21 +00:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
*aTransactionList = (nsITransactionList *)new nsTransactionList(this, &mRedoStack);
|
1998-11-24 00:45:21 +00:00
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
NS_IF_ADDREF(*aTransactionList);
|
1998-11-24 00:45:21 +00:00
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
return (! *aTransactionList) ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
|
1998-11-20 00:43:07 +00:00
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1998-11-20 00:43:07 +00:00
|
|
|
nsTransactionManager::AddListener(nsITransactionListener *aListener)
|
|
|
|
{
|
1999-05-26 21:16:25 +00:00
|
|
|
if (!aListener)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
if (!mListeners) {
|
2001-09-12 14:59:11 +00:00
|
|
|
mListeners = new nsAutoVoidArray();
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
if (!mListeners) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mListeners->AppendElement((void *)aListener)) {
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ADDREF(aListener);
|
|
|
|
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return NS_OK;
|
1998-11-20 00:43:07 +00:00
|
|
|
}
|
|
|
|
|
1999-05-27 20:48:55 +00:00
|
|
|
NS_IMETHODIMP
|
1998-11-20 00:43:07 +00:00
|
|
|
nsTransactionManager::RemoveListener(nsITransactionListener *aListener)
|
1998-11-19 21:43:21 +00:00
|
|
|
{
|
1999-05-26 21:16:25 +00:00
|
|
|
if (!aListener)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
1999-06-03 15:26:48 +00:00
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
if (!mListeners->RemoveElement((void *)aListener))
|
1999-06-03 15:26:48 +00:00
|
|
|
{
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
1999-05-26 21:16:25 +00:00
|
|
|
return NS_ERROR_FAILURE;
|
1999-06-03 15:26:48 +00:00
|
|
|
}
|
1999-05-26 21:16:25 +00:00
|
|
|
|
|
|
|
NS_IF_RELEASE(aListener);
|
|
|
|
|
|
|
|
if (mListeners->Count() < 1)
|
|
|
|
{
|
|
|
|
delete mListeners;
|
|
|
|
mListeners = 0;
|
|
|
|
}
|
|
|
|
|
1999-06-03 15:26:48 +00:00
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
return NS_OK;
|
1998-11-24 00:45:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::ClearUndoStack()
|
|
|
|
{
|
1998-12-02 17:39:09 +00:00
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
result = mUndoStack.Clear();
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return result;
|
1998-11-19 21:43:21 +00:00
|
|
|
}
|
|
|
|
|
1998-11-21 01:02:55 +00:00
|
|
|
nsresult
|
1998-11-24 00:45:21 +00:00
|
|
|
nsTransactionManager::ClearRedoStack()
|
1998-11-21 01:02:55 +00:00
|
|
|
{
|
1998-12-02 17:39:09 +00:00
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
LOCK_TX_MANAGER(this);
|
|
|
|
result = mRedoStack.Clear();
|
|
|
|
UNLOCK_TX_MANAGER(this);
|
|
|
|
|
|
|
|
return result;
|
1998-11-21 01:02:55 +00:00
|
|
|
}
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
nsresult
|
1999-11-11 19:35:40 +00:00
|
|
|
nsTransactionManager::WillDoNotify(nsITransaction *aTransaction, PRBool *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
result = listener->WillDo(this, aTransaction, aInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (NS_FAILED(result) || *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::DidDoNotify(nsITransaction *aTransaction, nsresult aDoResult)
|
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
result = listener->DidDo(this, aTransaction, aDoResult);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
1999-11-11 19:35:40 +00:00
|
|
|
nsTransactionManager::WillUndoNotify(nsITransaction *aTransaction, PRBool *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
result = listener->WillUndo(this, aTransaction, aInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (NS_FAILED(result) || *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::DidUndoNotify(nsITransaction *aTransaction, nsresult aUndoResult)
|
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
result = listener->DidUndo(this, aTransaction, aUndoResult);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
1999-11-11 19:35:40 +00:00
|
|
|
nsTransactionManager::WillRedoNotify(nsITransaction *aTransaction, PRBool *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
result = listener->WillRedo(this, aTransaction, aInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (NS_FAILED(result) || *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::DidRedoNotify(nsITransaction *aTransaction, nsresult aRedoResult)
|
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
result = listener->DidRedo(this, aTransaction, aRedoResult);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
1999-11-11 19:35:40 +00:00
|
|
|
nsTransactionManager::WillBeginBatchNotify(PRBool *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
result = listener->WillBeginBatch(this, aInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (NS_FAILED(result) || *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::DidBeginBatchNotify(nsresult aResult)
|
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
result = listener->DidBeginBatch(this, aResult);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
1999-11-11 19:35:40 +00:00
|
|
|
nsTransactionManager::WillEndBatchNotify(PRBool *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
result = listener->WillEndBatch(this, aInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (NS_FAILED(result) || *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::DidEndBatchNotify(nsresult aResult)
|
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
result = listener->DidEndBatch(this, aResult);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
1999-11-11 19:35:40 +00:00
|
|
|
nsTransactionManager::WillMergeNotify(nsITransaction *aTop, nsITransaction *aTransaction, PRBool *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
result = listener->WillMerge(this, aTop, aTransaction, aInterrupt);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (NS_FAILED(result) || *aInterrupt)
|
1999-05-26 21:16:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::DidMergeNotify(nsITransaction *aTop,
|
|
|
|
nsITransaction *aTransaction,
|
|
|
|
PRBool aDidMerge,
|
|
|
|
nsresult aMergeResult)
|
|
|
|
{
|
|
|
|
if (!mListeners)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
PRInt32 i, lcount = mListeners->Count();
|
|
|
|
|
|
|
|
for (i = 0; i < lcount; i++)
|
|
|
|
{
|
|
|
|
nsITransactionListener *listener = (nsITransactionListener *)mListeners->ElementAt(i);
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
result = listener->DidMerge(this, aTop, aTransaction, aDidMerge, aMergeResult);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-02-08 17:28:33 +00:00
|
|
|
nsresult
|
|
|
|
nsTransactionManager::BeginTransaction(nsITransaction *aTransaction)
|
|
|
|
{
|
|
|
|
nsTransactionItem *tx;
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
|
|
|
|
// No need for LOCK/UNLOCK_TX_MANAGER() calls since the calling routine
|
|
|
|
// should have done this already!
|
|
|
|
|
|
|
|
NS_IF_ADDREF(aTransaction);
|
|
|
|
|
|
|
|
// XXX: POSSIBLE OPTIMIZATION
|
|
|
|
// We could use a factory that pre-allocates/recycles transaction items.
|
|
|
|
tx = new nsTransactionItem(aTransaction);
|
|
|
|
|
|
|
|
if (!tx) {
|
|
|
|
NS_IF_RELEASE(aTransaction);
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = mDoStack.Push(tx);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
delete tx;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2001-03-09 14:23:59 +00:00
|
|
|
result = tx->DoTransaction();
|
1999-02-08 17:28:33 +00:00
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
mDoStack.Pop(&tx);
|
|
|
|
delete tx;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::EndTransaction()
|
|
|
|
{
|
|
|
|
nsITransaction *tint = 0;
|
|
|
|
nsTransactionItem *tx = 0;
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
|
|
|
|
// No need for LOCK/UNLOCK_TX_MANAGER() calls since the calling routine
|
|
|
|
// should have done this already!
|
|
|
|
|
|
|
|
result = mDoStack.Pop(&tx);
|
|
|
|
|
|
|
|
if (NS_FAILED(result) || !tx)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
result = tx->GetTransaction(&tint);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
// XXX: What do we do with the transaction item at this point?
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tint) {
|
|
|
|
PRInt32 nc = 0;
|
|
|
|
|
|
|
|
// If we get here, the transaction must be a dummy batch transaction
|
|
|
|
// created by BeginBatch(). If it contains no children, get rid of it!
|
|
|
|
|
|
|
|
tx->GetNumberOfChildren(&nc);
|
|
|
|
|
|
|
|
if (!nc) {
|
|
|
|
delete tx;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the transaction is transient. If it is, there's nothing
|
|
|
|
// more to do, just return.
|
|
|
|
|
|
|
|
PRBool isTransient = PR_FALSE;
|
|
|
|
|
|
|
|
if (tint)
|
|
|
|
result = tint->GetIsTransient(&isTransient);
|
|
|
|
|
|
|
|
if (NS_FAILED(result) || isTransient || !mMaxTransactionCount) {
|
|
|
|
// XXX: Should we be clearing the redo stack if the transaction
|
|
|
|
// is transient and there is nothing on the do stack?
|
|
|
|
delete tx;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsTransactionItem *top = 0;
|
|
|
|
|
|
|
|
// Check if there is a transaction on the do stack. If there is,
|
|
|
|
// the current transaction is a "sub" transaction, and should
|
|
|
|
// be added to the transaction at the top of the do stack.
|
|
|
|
|
|
|
|
result = mDoStack.Peek(&top);
|
|
|
|
if (top) {
|
|
|
|
result = top->AddChild(tx);
|
|
|
|
|
|
|
|
// XXX: What do we do if this fails?
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The transaction succeeded, so clear the redo stack.
|
|
|
|
|
|
|
|
result = ClearRedoStack();
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
// XXX: What do we do if this fails?
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we can coalesce this transaction with the one at the top
|
|
|
|
// of the undo stack.
|
|
|
|
|
|
|
|
top = 0;
|
|
|
|
result = mUndoStack.Peek(&top);
|
|
|
|
|
|
|
|
if (tint && top) {
|
|
|
|
PRBool didMerge = PR_FALSE;
|
|
|
|
nsITransaction *topTransaction = 0;
|
|
|
|
|
|
|
|
result = top->GetTransaction(&topTransaction);
|
|
|
|
|
|
|
|
if (topTransaction) {
|
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
PRBool doInterrupt = PR_FALSE;
|
|
|
|
|
|
|
|
result = WillMergeNotify(topTransaction, tint, &doInterrupt);
|
1999-02-08 17:28:33 +00:00
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
if (NS_FAILED(result))
|
1999-02-08 17:28:33 +00:00
|
|
|
return result;
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1999-11-11 19:35:40 +00:00
|
|
|
if (!doInterrupt) {
|
2001-03-09 14:23:59 +00:00
|
|
|
result = topTransaction->Merge(tint, &didMerge);
|
1999-05-26 21:16:25 +00:00
|
|
|
|
1999-06-03 15:26:48 +00:00
|
|
|
nsresult result2 = DidMergeNotify(topTransaction, tint, didMerge, result);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = result2;
|
|
|
|
|
1999-05-26 21:16:25 +00:00
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
// XXX: What do we do if this fails?
|
|
|
|
}
|
|
|
|
|
|
|
|
if (didMerge) {
|
|
|
|
delete tx;
|
|
|
|
return result;
|
|
|
|
}
|
1999-02-08 17:28:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to see if we've hit the max level of undo. If so,
|
|
|
|
// pop the bottom transaction off the undo stack and release it!
|
|
|
|
|
|
|
|
PRInt32 sz = 0;
|
|
|
|
|
|
|
|
result = mUndoStack.GetSize(&sz);
|
|
|
|
|
|
|
|
if (mMaxTransactionCount > 0 && sz >= mMaxTransactionCount) {
|
|
|
|
nsTransactionItem *overflow = 0;
|
|
|
|
|
|
|
|
result = mUndoStack.PopBottom(&overflow);
|
|
|
|
|
|
|
|
// XXX: What do we do in the case where this fails?
|
|
|
|
|
|
|
|
if (overflow)
|
|
|
|
delete overflow;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push the transaction on the undo stack:
|
|
|
|
|
|
|
|
result = mUndoStack.Push(tx);
|
|
|
|
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
// XXX: What do we do in the case where a clear fails?
|
|
|
|
// Remove the transaction from the stack, and release it?
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-06-03 15:26:48 +00:00
|
|
|
nsresult
|
|
|
|
nsTransactionManager::Lock()
|
|
|
|
{
|
|
|
|
if (mMonitor)
|
|
|
|
PR_EnterMonitor(mMonitor);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTransactionManager::Unlock()
|
|
|
|
{
|
|
|
|
if (mMonitor)
|
|
|
|
PR_ExitMonitor(mMonitor);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|