00001 00032 // Generic interface for undo/redo circular buffer 00033 00034 #include "linden_common.h" 00035 00036 #include "llundo.h" 00037 #include "llerror.h" 00038 00039 00040 // TODO: 00041 // implement doubly linked circular list for ring buffer 00042 // this will allow us to easily change the size of an undo buffer on the fly 00043 00044 //----------------------------------------------------------------------------- 00045 // LLUndoBuffer() 00046 //----------------------------------------------------------------------------- 00047 LLUndoBuffer::LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count ) 00048 { 00049 mNextAction = 0; 00050 mLastAction = 0; 00051 mFirstAction = 0; 00052 mOperationID = 0; 00053 00054 mNumActions = initial_count; 00055 00056 mActions = new LLUndoAction *[initial_count]; 00057 00058 //initialize buffer with actions 00059 for (S32 i = 0; i < initial_count; i++) 00060 { 00061 mActions[i] = create_func(); 00062 if (!mActions[i]) 00063 { 00064 llerrs << "Unable to create action for undo buffer" << llendl; 00065 } 00066 } 00067 } 00068 00069 //----------------------------------------------------------------------------- 00070 // ~LLUndoBuffer() 00071 //----------------------------------------------------------------------------- 00072 LLUndoBuffer::~LLUndoBuffer() 00073 { 00074 for (S32 i = 0; i < mNumActions; i++) 00075 { 00076 delete mActions[i]; 00077 } 00078 00079 delete [] mActions; 00080 } 00081 00082 //----------------------------------------------------------------------------- 00083 // getNextAction() 00084 //----------------------------------------------------------------------------- 00085 LLUndoAction *LLUndoBuffer::getNextAction(BOOL setClusterBegin) 00086 { 00087 LLUndoAction *nextAction = mActions[mNextAction]; 00088 00089 if (setClusterBegin) 00090 { 00091 mOperationID++; 00092 } 00093 mActions[mNextAction]->mClusterID = mOperationID; 00094 00095 mNextAction = (mNextAction + 1) % mNumActions; 00096 mLastAction = mNextAction; 00097 00098 if (mNextAction == mFirstAction) 00099 { 00100 mActions[mFirstAction]->cleanup(); 00101 mFirstAction = (mFirstAction + 1) % mNumActions; 00102 } 00103 00104 return nextAction; 00105 } 00106 00107 //----------------------------------------------------------------------------- 00108 // undoAction() 00109 //----------------------------------------------------------------------------- 00110 BOOL LLUndoBuffer::undoAction() 00111 { 00112 if (!canUndo()) 00113 { 00114 return FALSE; 00115 } 00116 00117 S32 prevAction = (mNextAction + mNumActions - 1) % mNumActions; 00118 00119 while(mActions[prevAction]->mClusterID == mOperationID) 00120 { 00121 // go ahead and decrement action index 00122 mNextAction = prevAction; 00123 00124 // undo this action 00125 mActions[mNextAction]->undo(); 00126 00127 // we're at the first action, so we don't know if we've actually undid everything 00128 if (mNextAction == mFirstAction) 00129 { 00130 mOperationID--; 00131 return FALSE; 00132 } 00133 00134 // do wrap-around of index, but avoid negative numbers for modulo operator 00135 prevAction = (mNextAction + mNumActions - 1) % mNumActions; 00136 } 00137 00138 mOperationID--; 00139 00140 return TRUE; 00141 } 00142 00143 //----------------------------------------------------------------------------- 00144 // redoAction() 00145 //----------------------------------------------------------------------------- 00146 BOOL LLUndoBuffer::redoAction() 00147 { 00148 if (!canRedo()) 00149 { 00150 return FALSE; 00151 } 00152 00153 mOperationID++; 00154 00155 while(mActions[mNextAction]->mClusterID == mOperationID) 00156 { 00157 if (mNextAction == mLastAction) 00158 { 00159 return FALSE; 00160 } 00161 00162 mActions[mNextAction]->redo(); 00163 00164 // do wrap-around of index 00165 mNextAction = (mNextAction + 1) % mNumActions; 00166 } 00167 00168 return TRUE; 00169 } 00170 00171 //----------------------------------------------------------------------------- 00172 // flushActions() 00173 //----------------------------------------------------------------------------- 00174 void LLUndoBuffer::flushActions() 00175 { 00176 for (S32 i = 0; i < mNumActions; i++) 00177 { 00178 mActions[i]->cleanup(); 00179 } 00180 mNextAction = 0; 00181 mLastAction = 0; 00182 mFirstAction = 0; 00183 mOperationID = 0; 00184 }