/** * @file llundo.cpp * @brief LLUndo class * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2008, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ // Generic interface for undo/redo circular buffer #include "linden_common.h" #include "llundo.h" #include "llerror.h" // TODO: // implement doubly linked circular list for ring buffer // this will allow us to easily change the size of an undo buffer on the fly //----------------------------------------------------------------------------- // LLUndoBuffer() //----------------------------------------------------------------------------- LLUndoBuffer::LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count ) { mNextAction = 0; mLastAction = 0; mFirstAction = 0; mOperationID = 0; mNumActions = initial_count; mActions = new LLUndoAction *[initial_count]; //initialize buffer with actions for (S32 i = 0; i < initial_count; i++) { mActions[i] = create_func(); if (!mActions[i]) { llerrs << "Unable to create action for undo buffer" << llendl; } } } //----------------------------------------------------------------------------- // ~LLUndoBuffer() //----------------------------------------------------------------------------- LLUndoBuffer::~LLUndoBuffer() { for (S32 i = 0; i < mNumActions; i++) { delete mActions[i]; } delete [] mActions; } //----------------------------------------------------------------------------- // getNextAction() //----------------------------------------------------------------------------- LLUndoAction *LLUndoBuffer::getNextAction(BOOL setClusterBegin) { LLUndoAction *nextAction = mActions[mNextAction]; if (setClusterBegin) { mOperationID++; } mActions[mNextAction]->mClusterID = mOperationID; mNextAction = (mNextAction + 1) % mNumActions; mLastAction = mNextAction; if (mNextAction == mFirstAction) { mActions[mFirstAction]->cleanup(); mFirstAction = (mFirstAction + 1) % mNumActions; } return nextAction; } //----------------------------------------------------------------------------- // undoAction() //----------------------------------------------------------------------------- BOOL LLUndoBuffer::undoAction() { if (!canUndo()) { return FALSE; } S32 prevAction = (mNextAction + mNumActions - 1) % mNumActions; while(mActions[prevAction]->mClusterID == mOperationID) { // go ahead and decrement action index mNextAction = prevAction; // undo this action mActions[mNextAction]->undo(); // we're at the first action, so we don't know if we've actually undid everything if (mNextAction == mFirstAction) { mOperationID--; return FALSE; } // do wrap-around of index, but avoid negative numbers for modulo operator prevAction = (mNextAction + mNumActions - 1) % mNumActions; } mOperationID--; return TRUE; } //----------------------------------------------------------------------------- // redoAction() //----------------------------------------------------------------------------- BOOL LLUndoBuffer::redoAction() { if (!canRedo()) { return FALSE; } mOperationID++; while(mActions[mNextAction]->mClusterID == mOperationID) { if (mNextAction == mLastAction) { return FALSE; } mActions[mNextAction]->redo(); // do wrap-around of index mNextAction = (mNextAction + 1) % mNumActions; } return TRUE; } //----------------------------------------------------------------------------- // flushActions() //----------------------------------------------------------------------------- void LLUndoBuffer::flushActions() { for (S32 i = 0; i < mNumActions; i++) { mActions[i]->cleanup(); } mNextAction = 0; mLastAction = 0; mFirstAction = 0; mOperationID = 0; }