Back to index

lightning-sunbird  0.9+nobinonly
nsTransactionItem.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsITransaction.h"
00039 #include "nsTransactionStack.h"
00040 #include "nsTransactionManager.h"
00041 #include "nsTransactionItem.h"
00042 #include "nsCOMPtr.h"
00043 
00044 nsTransactionItem::nsTransactionItem(nsITransaction *aTransaction)
00045     : mTransaction(aTransaction), mUndoStack(0), mRedoStack(0)
00046 {
00047 }
00048 
00049 nsTransactionItem::~nsTransactionItem()
00050 {
00051   if (mRedoStack)
00052     delete mRedoStack;
00053 
00054   if (mUndoStack)
00055     delete mUndoStack;
00056 
00057   NS_IF_RELEASE(mTransaction);
00058 }
00059 
00060 nsresult
00061 nsTransactionItem::AddChild(nsTransactionItem *aTransactionItem)
00062 {
00063   if (!aTransactionItem)
00064     return NS_ERROR_NULL_POINTER;
00065 
00066   if (!mUndoStack) {
00067     mUndoStack = new nsTransactionStack();
00068     if (!mUndoStack)
00069       return NS_ERROR_OUT_OF_MEMORY;
00070   }
00071 
00072   mUndoStack->Push(aTransactionItem);
00073 
00074   return NS_OK;
00075 }
00076 
00077 nsresult
00078 nsTransactionItem::GetTransaction(nsITransaction **aTransaction)
00079 {
00080   if (!aTransaction)
00081     return NS_ERROR_NULL_POINTER;
00082 
00083   *aTransaction = mTransaction;
00084 
00085   return NS_OK;
00086 }
00087 
00088 nsresult
00089 nsTransactionItem::GetIsBatch(PRBool *aIsBatch)
00090 {
00091   if (!aIsBatch)
00092     return NS_ERROR_NULL_POINTER;
00093 
00094   *aIsBatch = !mTransaction;
00095 
00096   return NS_OK;
00097 }
00098 
00099 nsresult
00100 nsTransactionItem::GetNumberOfChildren(PRInt32 *aNumChildren)
00101 {
00102   nsresult result;
00103 
00104   if (!aNumChildren)
00105     return NS_ERROR_NULL_POINTER;
00106 
00107   *aNumChildren = 0;
00108 
00109   PRInt32 ui = 0;
00110   PRInt32 ri = 0;
00111 
00112   result = GetNumberOfUndoItems(&ui);
00113 
00114   if (NS_FAILED(result))
00115     return result;
00116 
00117   result = GetNumberOfRedoItems(&ri);
00118 
00119   if (NS_FAILED(result))
00120     return result;
00121 
00122   *aNumChildren = ui + ri;
00123 
00124   return NS_OK;
00125 }
00126 
00127 nsresult
00128 nsTransactionItem::GetChild(PRInt32 aIndex, nsTransactionItem **aChild)
00129 {
00130   if (!aChild)
00131     return NS_ERROR_NULL_POINTER;
00132 
00133   *aChild = 0;
00134 
00135   PRInt32 numItems = 0;
00136   nsresult result = GetNumberOfChildren(&numItems);
00137 
00138   if (NS_FAILED(result))
00139     return result;
00140 
00141   if (aIndex < 0 || aIndex >= numItems)
00142     return NS_ERROR_FAILURE;
00143 
00144   // Children are expected to be in the order they were added,
00145   // so the child first added would be at the bottom of the undo
00146   // stack, or if there are no items on the undo stack, it would
00147   // be at the top of the redo stack.
00148 
00149   result = GetNumberOfUndoItems(&numItems);
00150 
00151   if (NS_FAILED(result))
00152     return result;
00153 
00154   if (numItems > 0 && aIndex < numItems) {
00155     if (!mUndoStack)
00156       return NS_ERROR_FAILURE;
00157 
00158     return mUndoStack->GetItem(aIndex, aChild);
00159   }
00160 
00161   // Adjust the index for the redo stack:
00162 
00163   aIndex -=  numItems;
00164 
00165   result = GetNumberOfRedoItems(&numItems);
00166 
00167   if (NS_FAILED(result))
00168     return result;
00169 
00170   if (!mRedoStack || numItems == 0 || aIndex >= numItems)
00171       return NS_ERROR_FAILURE;
00172 
00173   return mRedoStack->GetItem(numItems - aIndex - 1, aChild);
00174 }
00175 
00176 nsresult
00177 nsTransactionItem::DoTransaction()
00178 {
00179   if (mTransaction)
00180     return mTransaction->DoTransaction();
00181   return NS_OK;
00182 }
00183 
00184 nsresult
00185 nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr)
00186 {
00187   nsresult result = UndoChildren(aTxMgr);
00188 
00189   if (NS_FAILED(result)) {
00190     RecoverFromUndoError(aTxMgr);
00191     return result;
00192   }
00193 
00194   if (!mTransaction)
00195     return NS_OK;
00196 
00197   result = mTransaction->UndoTransaction();
00198 
00199   if (NS_FAILED(result)) {
00200     RecoverFromUndoError(aTxMgr);
00201     return result;
00202   }
00203 
00204   return NS_OK;
00205 }
00206 
00207 nsresult
00208 nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
00209 {
00210   nsTransactionItem *item;
00211   nsresult result = NS_OK;
00212   PRInt32 sz = 0;
00213 
00214   if (mUndoStack) {
00215     if (!mRedoStack && mUndoStack) {
00216       mRedoStack = new nsTransactionRedoStack();
00217       if (!mRedoStack)
00218         return NS_ERROR_OUT_OF_MEMORY;
00219     }
00220 
00221     /* Undo all of the transaction items children! */
00222     result = mUndoStack->GetSize(&sz);
00223 
00224     if (NS_FAILED(result))
00225       return result;
00226 
00227     while (sz-- > 0) {
00228       result = mUndoStack->Peek(&item);
00229 
00230       if (NS_FAILED(result)) {
00231         return result;
00232       }
00233 
00234       nsITransaction *t = 0;
00235 
00236       result = item->GetTransaction(&t);
00237 
00238       if (NS_FAILED(result)) {
00239         return result;
00240       }
00241 
00242       PRBool doInterrupt = PR_FALSE;
00243 
00244       result = aTxMgr->WillUndoNotify(t, &doInterrupt);
00245 
00246       if (NS_FAILED(result)) {
00247         return result;
00248       }
00249 
00250       if (doInterrupt) {
00251         return NS_OK;
00252       }
00253 
00254       result = item->UndoTransaction(aTxMgr);
00255 
00256       if (NS_SUCCEEDED(result)) {
00257         result = mUndoStack->Pop(&item);
00258 
00259         if (NS_SUCCEEDED(result)) {
00260           result = mRedoStack->Push(item);
00261 
00262           /* XXX: If we got an error here, I doubt we can recover!
00263            * XXX: Should we just push the item back on the undo stack?
00264            */
00265         }
00266       }
00267 
00268       nsresult result2 = aTxMgr->DidUndoNotify(t, result);
00269 
00270       if (NS_SUCCEEDED(result)) {
00271         result = result2;
00272       }
00273     }
00274   }
00275 
00276   return result;
00277 }
00278 
00279 nsresult
00280 nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr)
00281 {
00282   nsresult result;
00283 
00284   if (mTransaction) {
00285     result = mTransaction->RedoTransaction();
00286 
00287     if (NS_FAILED(result))
00288       return result;
00289   }
00290 
00291   result = RedoChildren(aTxMgr);
00292 
00293   if (NS_FAILED(result)) {
00294     RecoverFromRedoError(aTxMgr);
00295     return result;
00296   }
00297 
00298   return NS_OK;
00299 }
00300 
00301 nsresult
00302 nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
00303 {
00304   nsTransactionItem *item;
00305   nsresult result = NS_OK;
00306   PRInt32 sz = 0;
00307 
00308   if (!mRedoStack)
00309     return NS_OK;
00310 
00311   /* Redo all of the transaction items children! */
00312   result = mRedoStack->GetSize(&sz);
00313 
00314   if (NS_FAILED(result))
00315     return result;
00316 
00317 
00318   while (sz-- > 0) {
00319     result = mRedoStack->Peek(&item);
00320 
00321     if (NS_FAILED(result)) {
00322       return result;
00323     }
00324 
00325     nsITransaction *t = 0;
00326 
00327     result = item->GetTransaction(&t);
00328 
00329     if (NS_FAILED(result)) {
00330       return result;
00331     }
00332 
00333     PRBool doInterrupt = PR_FALSE;
00334 
00335     result = aTxMgr->WillRedoNotify(t, &doInterrupt);
00336 
00337     if (NS_FAILED(result)) {
00338       return result;
00339     }
00340 
00341     if (doInterrupt) {
00342       return NS_OK;
00343     }
00344 
00345     result = item->RedoTransaction(aTxMgr);
00346 
00347     if (NS_SUCCEEDED(result)) {
00348       result = mRedoStack->Pop(&item);
00349 
00350       if (NS_SUCCEEDED(result)) {
00351         result = mUndoStack->Push(item);
00352 
00353         // XXX: If we got an error here, I doubt we can recover!
00354         // XXX: Should we just push the item back on the redo stack?
00355       }
00356     }
00357 
00358     nsresult result2 = aTxMgr->DidUndoNotify(t, result);
00359 
00360     if (NS_SUCCEEDED(result)) {
00361       result = result2;
00362     }
00363   }
00364 
00365   return result;
00366 }
00367 
00368 nsresult
00369 nsTransactionItem::GetNumberOfUndoItems(PRInt32 *aNumItems)
00370 {
00371   if (!aNumItems)
00372     return NS_ERROR_NULL_POINTER;
00373 
00374   if (!mUndoStack) {
00375     *aNumItems = 0;
00376     return NS_OK;
00377   }
00378 
00379   return mUndoStack->GetSize(aNumItems);
00380 }
00381 
00382 nsresult
00383 nsTransactionItem::GetNumberOfRedoItems(PRInt32 *aNumItems)
00384 {
00385   if (!aNumItems)
00386     return NS_ERROR_NULL_POINTER;
00387 
00388   if (!mRedoStack) {
00389     *aNumItems = 0;
00390     return NS_OK;
00391   }
00392 
00393   return mRedoStack->GetSize(aNumItems);
00394 }
00395 
00396 nsresult
00397 nsTransactionItem::RecoverFromUndoError(nsTransactionManager *aTxMgr)
00398 {
00399   //
00400   // If this method gets called, we never got to the point where we
00401   // successfully called UndoTransaction() for the transaction item itself.
00402   // Just redo any children that successfully called undo!
00403   //
00404   return RedoChildren(aTxMgr);
00405 }
00406 
00407 nsresult
00408 nsTransactionItem::RecoverFromRedoError(nsTransactionManager *aTxMgr)
00409 {
00410   //
00411   // If this method gets called, we already successfully called
00412   // RedoTransaction() for the transaction item itself. Undo all
00413   // the children that successfully called RedoTransaction(),
00414   // then undo the transaction item itself.
00415   //
00416 
00417   nsresult result;
00418 
00419   result = UndoChildren(aTxMgr);
00420 
00421   if (NS_FAILED(result)) {
00422     return result;
00423   }
00424 
00425   if (!mTransaction)
00426     return NS_OK;
00427 
00428   return mTransaction->UndoTransaction();
00429 }
00430