NS_IMETHODIMP nsTransactionManager::PeekRedoStack(nsITransaction **aTransaction) { nsRefPtr<nsTransactionItem> tx; nsresult result; NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER); *aTransaction = 0; LOCK_TX_MANAGER(this); result = mRedoStack.Peek(getter_AddRefs(tx)); if (NS_FAILED(result) || !tx) { UNLOCK_TX_MANAGER(this); return result; } result = tx->GetTransaction(aTransaction); UNLOCK_TX_MANAGER(this); return result; }
NS_IMETHODIMP nsTransactionManager::EndBatch() { nsRefPtr<nsTransactionItem> tx; nsCOMPtr<nsITransaction> ti; 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 // the DoTransaction() method of a transaction. // // 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(getter_AddRefs(tx)); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } if (tx) tx->GetTransaction(getter_AddRefs(ti)); if (!tx || ti) { UNLOCK_TX_MANAGER(this); return NS_ERROR_FAILURE; } PRBool doInterrupt = PR_FALSE; result = WillEndBatchNotify(&doInterrupt); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } if (doInterrupt) { UNLOCK_TX_MANAGER(this); return NS_OK; } result = EndTransaction(); nsresult result2 = DidEndBatchNotify(result); if (NS_SUCCEEDED(result)) result = result2; UNLOCK_TX_MANAGER(this); return result; }
nsresult nsTransactionManager::ClearRedoStack() { nsresult result; LOCK_TX_MANAGER(this); result = mRedoStack.Clear(); UNLOCK_TX_MANAGER(this); return result; }
NS_IMETHODIMP nsTransactionManager::GetMaxTransactionCount(PRInt32 *aMaxCount) { NS_ENSURE_TRUE(aMaxCount, NS_ERROR_NULL_POINTER); LOCK_TX_MANAGER(this); *aMaxCount = mMaxTransactionCount; UNLOCK_TX_MANAGER(this); return NS_OK; }
NS_IMETHODIMP nsTransactionManager::GetNumberOfRedoItems(PRInt32 *aNumItems) { nsresult result; LOCK_TX_MANAGER(this); result = mRedoStack.GetSize(aNumItems); UNLOCK_TX_MANAGER(this); return result; }
NS_IMETHODIMP nsTransactionManager::RemoveListener(nsITransactionListener *aListener) { NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER); LOCK_TX_MANAGER(this); nsresult rv = mListeners.RemoveObject(aListener) ? NS_OK : NS_ERROR_FAILURE; UNLOCK_TX_MANAGER(this); return rv; }
NS_IMETHODIMP nsTransactionManager::AddListener(nsITransactionListener *aListener) { if (!aListener) return NS_ERROR_NULL_POINTER; LOCK_TX_MANAGER(this); nsresult rv = mListeners.AppendObject(aListener) ? NS_OK : NS_ERROR_FAILURE; UNLOCK_TX_MANAGER(this); return rv; }
NS_IMETHODIMP nsTransactionManager::DoTransaction(nsITransaction *aTransaction) { nsresult result; if (!aTransaction) return NS_ERROR_NULL_POINTER; LOCK_TX_MANAGER(this); PRBool doInterrupt = PR_FALSE; result = WillDoNotify(aTransaction, &doInterrupt); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } if (doInterrupt) { UNLOCK_TX_MANAGER(this); return NS_OK; } result = BeginTransaction(aTransaction); if (NS_FAILED(result)) { DidDoNotify(aTransaction, result); UNLOCK_TX_MANAGER(this); return result; } result = EndTransaction(); nsresult result2 = DidDoNotify(aTransaction, result); if (NS_SUCCEEDED(result)) result = result2; UNLOCK_TX_MANAGER(this); return result; }
NS_IMETHODIMP nsTransactionManager::Clear() { nsresult result; LOCK_TX_MANAGER(this); result = ClearRedoStack(); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } result = ClearUndoStack(); UNLOCK_TX_MANAGER(this); return result; }
NS_IMETHODIMP 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); PRBool doInterrupt = PR_FALSE; result = WillBeginBatchNotify(&doInterrupt); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } if (doInterrupt) { UNLOCK_TX_MANAGER(this); return NS_OK; } result = BeginTransaction(0); nsresult result2 = DidBeginBatchNotify(result); if (NS_SUCCEEDED(result)) result = result2; UNLOCK_TX_MANAGER(this); return result; }
NS_IMETHODIMP nsTransactionManager::SetMaxTransactionCount(PRInt32 aMaxCount) { PRInt32 numUndoItems = 0, numRedoItems = 0, total = 0; nsRefPtr<nsTransactionItem> tx; nsresult result; LOCK_TX_MANAGER(this); // 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. result = mDoStack.Peek(getter_AddRefs(tx)); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } if (tx) { UNLOCK_TX_MANAGER(this); return NS_ERROR_FAILURE; } // 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) { result = mUndoStack.PopBottom(getter_AddRefs(tx)); if (NS_FAILED(result) || !tx) { UNLOCK_TX_MANAGER(this); return result; } --numUndoItems; } // If necessary, 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) { result = mRedoStack.PopBottom(getter_AddRefs(tx)); if (NS_FAILED(result) || !tx) { UNLOCK_TX_MANAGER(this); return result; } --numRedoItems; } mMaxTransactionCount = aMaxCount; UNLOCK_TX_MANAGER(this); return result; }
NS_IMETHODIMP nsTransactionManager::RedoTransaction() { nsresult result = NS_OK; nsRefPtr<nsTransactionItem> tx; LOCK_TX_MANAGER(this); // 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. result = mDoStack.Peek(getter_AddRefs(tx)); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } if (tx) { UNLOCK_TX_MANAGER(this); return NS_ERROR_FAILURE; } // Peek at the top of the redo stack. Don't remove the transaction // until it has successfully completed. result = mRedoStack.Peek(getter_AddRefs(tx)); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } // Bail if there's nothing on the stack. if (!tx) { UNLOCK_TX_MANAGER(this); return NS_OK; } nsCOMPtr<nsITransaction> t; result = tx->GetTransaction(getter_AddRefs(t)); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } PRBool doInterrupt = PR_FALSE; result = WillRedoNotify(t, &doInterrupt); if (NS_FAILED(result)) { UNLOCK_TX_MANAGER(this); return result; } if (doInterrupt) { UNLOCK_TX_MANAGER(this); return NS_OK; } result = tx->RedoTransaction(this); if (NS_SUCCEEDED(result)) { result = mRedoStack.Pop(getter_AddRefs(tx)); if (NS_SUCCEEDED(result)) result = mUndoStack.Push(tx); } nsresult result2 = DidRedoNotify(t, result); if (NS_SUCCEEDED(result)) result = result2; UNLOCK_TX_MANAGER(this); return result; }