2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// 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"
2014-11-04 03:42:51 +00:00
# include "console/engineAPI.h"
2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// 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 ( )
{
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 ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( CompoundUndoAction , addAction , void , ( const char * objName ) , , " addAction( UndoAction ) " )
2012-09-19 15:15:01 +00:00
{
UndoAction * action ;
2014-11-04 03:42:51 +00:00
if ( Sim : : findObject ( objName , action ) )
2012-09-19 15:15:01 +00:00
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 ( )
{
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 ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , clearAll , void , ( ) , , " Clears the undo manager. " )
2012-09-19 15:15:01 +00:00
{
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 ( ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , getUndoCount , S32 , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
return object - > getUndoCount ( ) ;
}
S32 UndoManager : : getUndoCount ( )
{
return mUndoStack . size ( ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , getUndoName , const char * , ( S32 index ) , , " (index) " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
return object - > getUndoName ( index ) ;
2012-09-19 15:15:01 +00:00
}
const char * UndoManager : : getUndoName ( S32 index )
{
if ( ( index < getUndoCount ( ) ) & & ( index > = 0 ) )
return mUndoStack [ index ] - > mActionName ;
return NULL ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , getUndoAction , S32 , ( S32 index ) , , " (index) " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
UndoAction * action = object - > getUndoAction ( index ) ;
2012-09-19 15:15:01 +00:00
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 ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , getRedoCount , S32 , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
return object - > getRedoCount ( ) ;
}
S32 UndoManager : : getRedoCount ( )
{
return mRedoStack . size ( ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , getRedoName , const char * , ( S32 index ) , , " (index) " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
return object - > getRedoName ( index ) ;
2012-09-19 15:15:01 +00:00
}
const char * UndoManager : : getRedoName ( S32 index )
{
if ( ( index < getRedoCount ( ) ) & & ( index > = 0 ) )
return mRedoStack [ getRedoCount ( ) - index - 1 ] - > mActionName ;
return NULL ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , getRedoAction , S32 , ( S32 index ) , , " (index) " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
UndoAction * action = object - > getRedoAction ( index ) ;
2012-09-19 15:15:01 +00:00
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 ) ;
}
//-----------------------------------------------------------------------------
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoAction , addToManager , void , ( const char * undoManager ) , ( " " ) , " action.addToManager([undoManager]) " )
2012-09-19 15:15:01 +00:00
{
UndoManager * theMan = NULL ;
2015-06-06 22:40:49 +00:00
if ( ! String : : isEmpty ( undoManager ) )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
SimObject * obj = Sim : : findObject ( undoManager ) ;
2012-09-19 15:15:01 +00:00
if ( obj )
theMan = dynamic_cast < UndoManager * > ( obj ) ;
}
object - > addToManager ( theMan ) ;
}
//-----------------------------------------------------------------------------
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoAction , undo , void , ( ) , , " () - Undo action contained in undo. " )
2012-09-19 15:15:01 +00:00
{
object - > undo ( ) ;
}
//-----------------------------------------------------------------------------
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoAction , redo , void , ( ) , , " () - Reo action contained in undo. " )
2012-09-19 15:15:01 +00:00
{
object - > redo ( ) ;
}
//-----------------------------------------------------------------------------
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , undo , void , ( ) , , " UndoManager.undo(); " )
2012-09-19 15:15:01 +00:00
{
object - > undo ( ) ;
}
//-----------------------------------------------------------------------------
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , redo , void , ( ) , , " UndoManager.redo(); " )
2012-09-19 15:15:01 +00:00
{
object - > redo ( ) ;
}
//-----------------------------------------------------------------------------
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , getNextUndoName , const char * , ( ) , , " UndoManager.getNextUndoName(); " )
2012-09-19 15:15:01 +00:00
{
const char * name = object - > getNextUndoName ( ) ;
if ( ! name )
return NULL ;
2018-03-09 01:59:40 +00:00
dsize_t retLen = dStrlen ( name ) + 1 ;
char * ret = Con : : getReturnBuffer ( retLen ) ;
dStrcpy ( ret , name , retLen ) ;
2012-09-19 15:15:01 +00:00
return ret ;
}
//-----------------------------------------------------------------------------
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , getNextRedoName , const char * , ( ) , , " UndoManager.getNextRedoName(); " )
2012-09-19 15:15:01 +00:00
{
const char * name = object - > getNextRedoName ( ) ;
if ( ! name )
return NULL ;
2018-03-09 01:59:40 +00:00
dsize_t retLen = dStrlen ( name ) + 1 ;
char * ret = Con : : getReturnBuffer ( retLen ) ;
dStrcpy ( ret , name , retLen ) ;
2012-09-19 15:15:01 +00:00
return ret ;
}
//-----------------------------------------------------------------------------
2019-08-03 15:53:10 +00:00
DefineEngineMethod ( UndoManager , pushCompound , const char * , ( String name ) , ( " " ) , " ( string name= \" \" ) - Push a CompoundUndoAction onto the compound stack for assembly. " )
2012-09-19 15:15:01 +00:00
{
CompoundUndoAction * action = object - > pushCompound ( name ) ;
if ( ! action )
return " " ;
if ( ! action - > isProperlyAdded ( ) )
action - > registerObject ( ) ;
return action - > getIdString ( ) ;
}
//-----------------------------------------------------------------------------
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( UndoManager , popCompound , void , ( bool discard ) , ( false ) , " ( bool discard=false ) - Pop the current CompoundUndoAction off the stack. " )
2012-09-19 15:15:01 +00:00
{
if ( ! object - > getCompoundStackDepth ( ) )
{
2014-11-05 00:51:13 +00:00
Con : : errorf ( " UndoManager::popCompound - no compound on stack (%s) " , object - > getName ( ) ) ;
2012-09-19 15:15:01 +00:00
return ;
}
object - > popCompound ( discard ) ;
}