123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "util/undo.h"
- #include "console/console.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- //-----------------------------------------------------------------------------
- // UndoAction
- //-----------------------------------------------------------------------------
- IMPLEMENT_CONOBJECT(UndoAction);
- IMPLEMENT_CONOBJECT(UndoScriptAction);
- ConsoleDocClass( UndoAction,
- "@brief An event which signals the editors to undo the last action\n\n"
- "Not intended for game development, for editors or internal use only.\n\n "
- "@internal");
- ConsoleDocClass( UndoScriptAction,
- "@brief Undo actions which can be created as script objects.\n\n"
- "Not intended for game development, for editors or internal use only.\n\n "
- "@internal");
- UndoAction::UndoAction(const UTF8 *actionName)
- {
- mActionName = actionName;
- mUndoManager = NULL;
- }
- UndoAction::~UndoAction()
- {
- // If we are registered to an undo manager, make sure
- // we get off its lists.
- if( mUndoManager )
- mUndoManager->removeAction( this, true );
- clearAllNotifications();
- }
- //-----------------------------------------------------------------------------
- void UndoAction::initPersistFields()
- {
- docsURL;
- addField("actionName", TypeRealString, Offset(mActionName, UndoAction),
- "A brief description of the action, for UI representation of this undo/redo action.");
- Parent::initPersistFields();
- }
- //-----------------------------------------------------------------------------
- void UndoAction::addToManager(UndoManager* theMan)
- {
- if(theMan)
- {
- mUndoManager = theMan;
- (*theMan).addAction(this);
- }
- else
- {
- mUndoManager = &UndoManager::getDefaultManager();
- mUndoManager->addAction(this);
- }
- }
- //-----------------------------------------------------------------------------
- // CompoundUndoAction
- //-----------------------------------------------------------------------------
- IMPLEMENT_CONOBJECT( CompoundUndoAction );
- ConsoleDocClass( CompoundUndoAction,
- "@brief An undo action that is comprised of other undo actions.\n\n"
- "Not intended for game development, for editors or internal use only.\n\n "
- "@internal");
- CompoundUndoAction::CompoundUndoAction( const UTF8 *actionName )
- : Parent( actionName )
- {
- }
- CompoundUndoAction::~CompoundUndoAction()
- {
- while( !mChildren.empty() )
- {
- UndoAction* action = mChildren.last();
- if( action->isProperlyAdded() )
- action->deleteObject();
- else
- {
- clearNotify( action ); // need to clear the delete notification manually in this case
- delete action;
- }
- mChildren.pop_back();
- }
- }
- void CompoundUndoAction::addAction( UndoAction *action )
- {
- //AssertFatal( action->mUndoManager == NULL, "CompoundUndoAction::addAction, action already had an UndoManager." );
- mChildren.push_back( action );
- deleteNotify( action );
- }
- void CompoundUndoAction::undo()
- {
- Vector<UndoAction*>::iterator itr = mChildren.end() - 1;
- for ( ; itr != mChildren.begin() - 1; itr-- )
- (*itr)->undo();
- }
- void CompoundUndoAction::redo()
- {
- Vector<UndoAction*>::iterator itr = mChildren.begin();
- for ( ; itr != mChildren.end(); itr++ )
- (*itr)->redo();
- }
- void CompoundUndoAction::onDeleteNotify( SimObject* object )
- {
- for( U32 i = 0; i < mChildren.size(); ++ i )
- if( mChildren[ i ] == object )
- mChildren.erase( i );
- Parent::onDeleteNotify( object );
- }
- DefineEngineMethod( CompoundUndoAction, addAction, void, (const char * objName), , "addAction( UndoAction )" )
- {
- UndoAction *action;
- if ( Sim::findObject( objName, action ) )
- object->addAction( action );
- }
- //-----------------------------------------------------------------------------
- // UndoManager
- //-----------------------------------------------------------------------------
- IMPLEMENT_CONOBJECT(UndoManager);
- ConsoleDocClass( UndoManager,
- "@brief SimObject which adds, tracks, and deletes UndoAction objects.\n\n"
- "Not intended for game development, for editors or internal use only.\n\n "
- "@internal")
- UndoManager::UndoManager(U32 levels)
- {
- VECTOR_SET_ASSOCIATION( mUndoStack );
- VECTOR_SET_ASSOCIATION( mRedoStack );
- VECTOR_SET_ASSOCIATION( mCompoundStack );
- mNumLevels = levels;
- // levels can be arbitrarily high, so we don't really want to reserve(levels).
- mUndoStack.reserve(10);
- mRedoStack.reserve(10);
- }
- //-----------------------------------------------------------------------------
- UndoManager::~UndoManager()
- {
- clearStack(mUndoStack);
- clearStack(mRedoStack);
- clearStack( *( ( Vector< UndoAction* >* ) &mCompoundStack ) );
- }
- //-----------------------------------------------------------------------------
- void UndoManager::initPersistFields()
- {
- docsURL;
- addField("numLevels", TypeS32, Offset(mNumLevels, UndoManager), "Number of undo & redo levels.");
- // arrange for the default undo manager to exist.
- // UndoManager &def = getDefaultManager();
- // Con::printf("def = %s undo manager created", def.getName());
-
- }
- //-----------------------------------------------------------------------------
- UndoManager& UndoManager::getDefaultManager()
- {
- // the default manager is created the first time it is asked for.
- static UndoManager *defaultMan = NULL;
- if(!defaultMan)
- {
- defaultMan = new UndoManager();
- defaultMan->assignName("DefaultUndoManager");
- defaultMan->registerObject();
- }
- return *defaultMan;
- }
- DefineEngineMethod(UndoManager, clearAll, void, (),, "Clears the undo manager.")
- {
- object->clearAll();
- }
- void UndoManager::clearAll()
- {
- clearStack(mUndoStack);
- clearStack(mRedoStack);
-
- Con::executef(this, "onClear");
- }
- //-----------------------------------------------------------------------------
- void UndoManager::clearStack(Vector<UndoAction*> &stack)
- {
- Vector<UndoAction*>::iterator itr = stack.begin();
- while (itr != stack.end())
- {
- UndoAction* undo = stack.first();
- stack.pop_front();
- // Call deleteObject() if the action was registered.
- if ( undo->isProperlyAdded() )
- undo->deleteObject();
- else
- delete undo;
- }
- stack.clear();
- }
- //-----------------------------------------------------------------------------
- void UndoManager::clampStack(Vector<UndoAction*> &stack)
- {
- while(stack.size() > mNumLevels)
- {
- UndoAction *act = stack.front();
- stack.pop_front();
- // Call deleteObject() if the action was registered.
- if ( act->isProperlyAdded() )
- act->deleteObject();
- else
- delete act;
- }
- }
- void UndoManager::removeAction(UndoAction *action, bool noDelete)
- {
- Vector<UndoAction*>::iterator itr = mUndoStack.begin();
- while (itr != mUndoStack.end())
- {
- if ((*itr) == action)
- {
- UndoAction* deleteAction = *itr;
- mUndoStack.erase(itr);
- doRemove( deleteAction, noDelete );
- return;
- }
- itr++;
- }
- itr = mRedoStack.begin();
- while (itr != mRedoStack.end())
- {
- if ((*itr) == action)
- {
- UndoAction* deleteAction = *itr;
- mRedoStack.erase(itr);
- doRemove( deleteAction, noDelete );
- return;
- }
- itr++;
- }
- }
- void UndoManager::doRemove( UndoAction* action, bool noDelete )
- {
- if( action->mUndoManager == this )
- action->mUndoManager = NULL;
- if( !noDelete )
- {
- // Call deleteObject() if the action was registered.
- if ( action->isProperlyAdded() )
- action->deleteObject();
- else
- delete action;
- }
- if( isProperlyAdded() )
- Con::executef(this, "onRemoveUndo");
- }
- //-----------------------------------------------------------------------------
- void UndoManager::undo()
- {
- // make sure we have an action available
- if(mUndoStack.size() < 1)
- return;
- // pop the action off the undo stack
- UndoAction *act = mUndoStack.last();
- mUndoStack.pop_back();
-
- // add it to the redo stack
- mRedoStack.push_back(act);
- if(mRedoStack.size() > mNumLevels)
- mRedoStack.pop_front();
-
- Con::executef(this, "onUndo");
- // perform the undo, whatever it may be.
- (*act).undo();
- }
- //-----------------------------------------------------------------------------
- void UndoManager::redo()
- {
- // make sure we have an action available
- if(mRedoStack.size() < 1)
- return;
- // pop the action off the redo stack
- UndoAction *react = mRedoStack.last();
- mRedoStack.pop_back();
-
- // add it to the undo stack
- mUndoStack.push_back(react);
- if(mUndoStack.size() > mNumLevels)
- mUndoStack.pop_front();
-
- Con::executef(this, "onRedo");
-
- // perform the redo, whatever it may be.
- (*react).redo();
- }
- DefineEngineMethod(UndoManager, getUndoCount, S32, (),, "")
- {
- return object->getUndoCount();
- }
- S32 UndoManager::getUndoCount()
- {
- return mUndoStack.size();
- }
- DefineEngineMethod(UndoManager, getUndoName, const char*, (S32 index), , "(index)")
- {
- return object->getUndoName(index);
- }
- const char* UndoManager::getUndoName(S32 index)
- {
- if ((index < getUndoCount()) && (index >= 0))
- return mUndoStack[index]->mActionName;
- return NULL;
- }
- DefineEngineMethod(UndoManager, getUndoAction, S32, (S32 index), , "(index)")
- {
- UndoAction * action = object->getUndoAction(index);
- if ( !action )
- return -1;
-
- if ( !action->isProperlyAdded() )
- action->registerObject();
- return action->getId();
- }
- UndoAction* UndoManager::getUndoAction(S32 index)
- {
- if ((index < getUndoCount()) && (index >= 0))
- return mUndoStack[index];
- return NULL;
- }
- DefineEngineMethod(UndoManager, getRedoCount, S32, (),, "")
- {
- return object->getRedoCount();
- }
- S32 UndoManager::getRedoCount()
- {
- return mRedoStack.size();
- }
- DefineEngineMethod(UndoManager, getRedoName, const char*, (S32 index), , "(index)")
- {
- return object->getRedoName(index);
- }
- const char* UndoManager::getRedoName(S32 index)
- {
- if ((index < getRedoCount()) && (index >= 0))
- return mRedoStack[getRedoCount() - index - 1]->mActionName;
- return NULL;
- }
- DefineEngineMethod(UndoManager, getRedoAction, S32, (S32 index), , "(index)")
- {
- UndoAction * action = object->getRedoAction(index);
- if ( !action )
- return -1;
- if ( !action->isProperlyAdded() )
- action->registerObject();
- return action->getId();
- }
- UndoAction* UndoManager::getRedoAction(S32 index)
- {
- if ((index < getRedoCount()) && (index >= 0))
- return mRedoStack[index];
- return NULL;
- }
- //-----------------------------------------------------------------------------
- const char* UndoManager::getNextUndoName()
- {
- if(mUndoStack.size() < 1)
- return NULL;
-
- UndoAction *act = mUndoStack.last();
- return (*act).mActionName;
- }
- //-----------------------------------------------------------------------------
- const char* UndoManager::getNextRedoName()
- {
- if(mRedoStack.size() < 1)
- return NULL;
- UndoAction *act = mRedoStack.last();
- return (*act).mActionName;
- }
- //-----------------------------------------------------------------------------
- void UndoManager::addAction(UndoAction* action)
- {
- // If we are assembling a compound, redirect the action to it
- // and don't modify our current undo/redo state.
-
- if( mCompoundStack.size() )
- {
- mCompoundStack.last()->addAction( action );
- return;
- }
-
- // clear the redo stack
- clearStack(mRedoStack);
- // push the incoming action onto the stack, move old data off the end if necessary.
- mUndoStack.push_back(action);
- if(mUndoStack.size() > mNumLevels)
- mUndoStack.pop_front();
- Con::executef(this, "onAddUndo");
- }
- //-----------------------------------------------------------------------------
- CompoundUndoAction* UndoManager::pushCompound( const String& name )
- {
- mCompoundStack.push_back( new CompoundUndoAction( name ) );
- return mCompoundStack.last();
- }
- //-----------------------------------------------------------------------------
- void UndoManager::popCompound( bool discard )
- {
- AssertFatal( getCompoundStackDepth() > 0, "UndoManager::popCompound - no compound on stack!" );
-
- CompoundUndoAction* undo = mCompoundStack.last();
- mCompoundStack.pop_back();
-
- if( discard || undo->getNumChildren() == 0 )
- {
- if( undo->isProperlyAdded() )
- undo->deleteObject();
- else
- delete undo;
- }
- else
- addAction( undo );
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod(UndoAction, addToManager, void, (const char * undoManager), (""), "action.addToManager([undoManager])")
- {
- UndoManager *theMan = NULL;
- if (!String::isEmpty(undoManager))
- {
- SimObject *obj = Sim::findObject(undoManager);
- if(obj)
- theMan = dynamic_cast<UndoManager*> (obj);
- }
- object->addToManager(theMan);
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( UndoAction, undo, void, (),, "() - Undo action contained in undo." )
- {
- object->undo();
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( UndoAction, redo, void, (),, "() - Reo action contained in undo." )
- {
- object->redo();
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod(UndoManager, undo, void, (),, "UndoManager.undo();")
- {
- object->undo();
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod(UndoManager, redo, void, (),, "UndoManager.redo();")
- {
- object->redo();
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod(UndoManager, getNextUndoName, const char *, (),, "UndoManager.getNextUndoName();")
- {
- const char *name = object->getNextUndoName();
- if(!name)
- return NULL;
- dsize_t retLen = dStrlen(name) + 1;
- char *ret = Con::getReturnBuffer(retLen);
- dStrcpy(ret, name, retLen);
- return ret;
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod(UndoManager, getNextRedoName, const char *, (),, "UndoManager.getNextRedoName();")
- {
- const char *name = object->getNextRedoName();
- if(!name)
- return NULL;
- dsize_t retLen = dStrlen(name) + 1;
- char *ret = Con::getReturnBuffer(retLen);
- dStrcpy(ret, name, retLen);
- return ret;
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( UndoManager, pushCompound, const char*, ( String name ), (""), "( string name=\"\" ) - Push a CompoundUndoAction onto the compound stack for assembly." )
- {
-
- CompoundUndoAction* action = object->pushCompound( name );
- if( !action )
- return "";
-
- if( !action->isProperlyAdded() )
- action->registerObject();
-
- return action->getIdString();
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( UndoManager, popCompound, void, ( bool discard ), (false), "( bool discard=false ) - Pop the current CompoundUndoAction off the stack." )
- {
- if( !object->getCompoundStackDepth() )
- {
- Con::errorf( "UndoManager::popCompound - no compound on stack (%s) ",object->getName() );
- return;
- }
-
-
- object->popCompound( discard );
- }
|