undo.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "util/undo.h"
  24. #include "console/console.h"
  25. #include "console/consoleTypes.h"
  26. //-----------------------------------------------------------------------------
  27. // UndoAction
  28. //-----------------------------------------------------------------------------
  29. IMPLEMENT_CONOBJECT(UndoAction);
  30. IMPLEMENT_CONOBJECT(UndoScriptAction);
  31. ConsoleDocClass( UndoAction,
  32. "@brief An event which signals the editors to undo the last action\n\n"
  33. "Not intended for game development, for editors or internal use only.\n\n "
  34. "@internal");
  35. ConsoleDocClass( UndoScriptAction,
  36. "@brief Undo actions which can be created as script objects.\n\n"
  37. "Not intended for game development, for editors or internal use only.\n\n "
  38. "@internal");
  39. UndoAction::UndoAction(const UTF8 *actionName)
  40. {
  41. mActionName = actionName;
  42. mUndoManager = NULL;
  43. }
  44. UndoAction::~UndoAction()
  45. {
  46. // If we are registered to an undo manager, make sure
  47. // we get off its lists.
  48. if( mUndoManager )
  49. mUndoManager->removeAction( this, true );
  50. clearAllNotifications();
  51. }
  52. //-----------------------------------------------------------------------------
  53. void UndoAction::initPersistFields()
  54. {
  55. addField("actionName", TypeRealString, Offset(mActionName, UndoAction),
  56. "A brief description of the action, for UI representation of this undo/redo action.");
  57. Parent::initPersistFields();
  58. }
  59. //-----------------------------------------------------------------------------
  60. void UndoAction::addToManager(UndoManager* theMan)
  61. {
  62. if(theMan)
  63. {
  64. mUndoManager = theMan;
  65. (*theMan).addAction(this);
  66. }
  67. else
  68. {
  69. mUndoManager = &UndoManager::getDefaultManager();
  70. mUndoManager->addAction(this);
  71. }
  72. }
  73. //-----------------------------------------------------------------------------
  74. // CompoundUndoAction
  75. //-----------------------------------------------------------------------------
  76. IMPLEMENT_CONOBJECT( CompoundUndoAction );
  77. ConsoleDocClass( CompoundUndoAction,
  78. "@brief An undo action that is comprised of other undo actions.\n\n"
  79. "Not intended for game development, for editors or internal use only.\n\n "
  80. "@internal");
  81. CompoundUndoAction::CompoundUndoAction( const UTF8 *actionName )
  82. : Parent( actionName )
  83. {
  84. }
  85. CompoundUndoAction::~CompoundUndoAction()
  86. {
  87. while( !mChildren.empty() )
  88. {
  89. UndoAction* action = mChildren.last();
  90. if( action->isProperlyAdded() )
  91. action->deleteObject();
  92. else
  93. {
  94. clearNotify( action ); // need to clear the delete notification manually in this case
  95. delete action;
  96. }
  97. mChildren.pop_back();
  98. }
  99. }
  100. void CompoundUndoAction::addAction( UndoAction *action )
  101. {
  102. //AssertFatal( action->mUndoManager == NULL, "CompoundUndoAction::addAction, action already had an UndoManager." );
  103. mChildren.push_back( action );
  104. deleteNotify( action );
  105. }
  106. void CompoundUndoAction::undo()
  107. {
  108. Vector<UndoAction*>::iterator itr = mChildren.end() - 1;
  109. for ( ; itr != mChildren.begin() - 1; itr-- )
  110. (*itr)->undo();
  111. }
  112. void CompoundUndoAction::redo()
  113. {
  114. Vector<UndoAction*>::iterator itr = mChildren.begin();
  115. for ( ; itr != mChildren.end(); itr++ )
  116. (*itr)->redo();
  117. }
  118. void CompoundUndoAction::onDeleteNotify( SimObject* object )
  119. {
  120. for( U32 i = 0; i < mChildren.size(); ++ i )
  121. if( mChildren[ i ] == object )
  122. mChildren.erase( i );
  123. Parent::onDeleteNotify( object );
  124. }
  125. ConsoleMethod( CompoundUndoAction, addAction, void, 3, 3, "addAction( UndoAction )" )
  126. {
  127. UndoAction *action;
  128. if ( Sim::findObject( argv[2], action ) )
  129. object->addAction( action );
  130. }
  131. //-----------------------------------------------------------------------------
  132. // UndoManager
  133. //-----------------------------------------------------------------------------
  134. IMPLEMENT_CONOBJECT(UndoManager);
  135. ConsoleDocClass( UndoManager,
  136. "@brief SimObject which adds, tracks, and deletes UndoAction objects.\n\n"
  137. "Not intended for game development, for editors or internal use only.\n\n "
  138. "@internal")
  139. UndoManager::UndoManager(U32 levels)
  140. {
  141. VECTOR_SET_ASSOCIATION( mUndoStack );
  142. VECTOR_SET_ASSOCIATION( mRedoStack );
  143. VECTOR_SET_ASSOCIATION( mCompoundStack );
  144. mNumLevels = levels;
  145. // levels can be arbitrarily high, so we don't really want to reserve(levels).
  146. mUndoStack.reserve(10);
  147. mRedoStack.reserve(10);
  148. }
  149. //-----------------------------------------------------------------------------
  150. UndoManager::~UndoManager()
  151. {
  152. clearStack(mUndoStack);
  153. clearStack(mRedoStack);
  154. clearStack( *( ( Vector< UndoAction* >* ) &mCompoundStack ) );
  155. }
  156. //-----------------------------------------------------------------------------
  157. void UndoManager::initPersistFields()
  158. {
  159. addField("numLevels", TypeS32, Offset(mNumLevels, UndoManager), "Number of undo & redo levels.");
  160. // arrange for the default undo manager to exist.
  161. // UndoManager &def = getDefaultManager();
  162. // Con::printf("def = %s undo manager created", def.getName());
  163. }
  164. //-----------------------------------------------------------------------------
  165. UndoManager& UndoManager::getDefaultManager()
  166. {
  167. // the default manager is created the first time it is asked for.
  168. static UndoManager *defaultMan = NULL;
  169. if(!defaultMan)
  170. {
  171. defaultMan = new UndoManager();
  172. defaultMan->assignName("DefaultUndoManager");
  173. defaultMan->registerObject();
  174. }
  175. return *defaultMan;
  176. }
  177. ConsoleMethod(UndoManager, clearAll, void, 2, 2, "Clears the undo manager.")
  178. {
  179. object->clearAll();
  180. }
  181. void UndoManager::clearAll()
  182. {
  183. clearStack(mUndoStack);
  184. clearStack(mRedoStack);
  185. Con::executef(this, "onClear");
  186. }
  187. //-----------------------------------------------------------------------------
  188. void UndoManager::clearStack(Vector<UndoAction*> &stack)
  189. {
  190. Vector<UndoAction*>::iterator itr = stack.begin();
  191. while (itr != stack.end())
  192. {
  193. UndoAction* undo = stack.first();
  194. stack.pop_front();
  195. // Call deleteObject() if the action was registered.
  196. if ( undo->isProperlyAdded() )
  197. undo->deleteObject();
  198. else
  199. delete undo;
  200. }
  201. stack.clear();
  202. }
  203. //-----------------------------------------------------------------------------
  204. void UndoManager::clampStack(Vector<UndoAction*> &stack)
  205. {
  206. while(stack.size() > mNumLevels)
  207. {
  208. UndoAction *act = stack.front();
  209. stack.pop_front();
  210. // Call deleteObject() if the action was registered.
  211. if ( act->isProperlyAdded() )
  212. act->deleteObject();
  213. else
  214. delete act;
  215. }
  216. }
  217. void UndoManager::removeAction(UndoAction *action, bool noDelete)
  218. {
  219. Vector<UndoAction*>::iterator itr = mUndoStack.begin();
  220. while (itr != mUndoStack.end())
  221. {
  222. if ((*itr) == action)
  223. {
  224. UndoAction* deleteAction = *itr;
  225. mUndoStack.erase(itr);
  226. doRemove( deleteAction, noDelete );
  227. return;
  228. }
  229. itr++;
  230. }
  231. itr = mRedoStack.begin();
  232. while (itr != mRedoStack.end())
  233. {
  234. if ((*itr) == action)
  235. {
  236. UndoAction* deleteAction = *itr;
  237. mRedoStack.erase(itr);
  238. doRemove( deleteAction, noDelete );
  239. return;
  240. }
  241. itr++;
  242. }
  243. }
  244. void UndoManager::doRemove( UndoAction* action, bool noDelete )
  245. {
  246. if( action->mUndoManager == this )
  247. action->mUndoManager = NULL;
  248. if( !noDelete )
  249. {
  250. // Call deleteObject() if the action was registered.
  251. if ( action->isProperlyAdded() )
  252. action->deleteObject();
  253. else
  254. delete action;
  255. }
  256. if( isProperlyAdded() )
  257. Con::executef(this, "onRemoveUndo");
  258. }
  259. //-----------------------------------------------------------------------------
  260. void UndoManager::undo()
  261. {
  262. // make sure we have an action available
  263. if(mUndoStack.size() < 1)
  264. return;
  265. // pop the action off the undo stack
  266. UndoAction *act = mUndoStack.last();
  267. mUndoStack.pop_back();
  268. // add it to the redo stack
  269. mRedoStack.push_back(act);
  270. if(mRedoStack.size() > mNumLevels)
  271. mRedoStack.pop_front();
  272. Con::executef(this, "onUndo");
  273. // perform the undo, whatever it may be.
  274. (*act).undo();
  275. }
  276. //-----------------------------------------------------------------------------
  277. void UndoManager::redo()
  278. {
  279. // make sure we have an action available
  280. if(mRedoStack.size() < 1)
  281. return;
  282. // pop the action off the redo stack
  283. UndoAction *react = mRedoStack.last();
  284. mRedoStack.pop_back();
  285. // add it to the undo stack
  286. mUndoStack.push_back(react);
  287. if(mUndoStack.size() > mNumLevels)
  288. mUndoStack.pop_front();
  289. Con::executef(this, "onRedo");
  290. // perform the redo, whatever it may be.
  291. (*react).redo();
  292. }
  293. ConsoleMethod(UndoManager, getUndoCount, S32, 2, 2, "")
  294. {
  295. return object->getUndoCount();
  296. }
  297. S32 UndoManager::getUndoCount()
  298. {
  299. return mUndoStack.size();
  300. }
  301. ConsoleMethod(UndoManager, getUndoName, const char*, 3, 3, "(index)")
  302. {
  303. return object->getUndoName(dAtoi(argv[2]));
  304. }
  305. const char* UndoManager::getUndoName(S32 index)
  306. {
  307. if ((index < getUndoCount()) && (index >= 0))
  308. return mUndoStack[index]->mActionName;
  309. return NULL;
  310. }
  311. ConsoleMethod(UndoManager, getUndoAction, S32, 3, 3, "(index)")
  312. {
  313. UndoAction * action = object->getUndoAction(dAtoi(argv[2]));
  314. if ( !action )
  315. return -1;
  316. if ( !action->isProperlyAdded() )
  317. action->registerObject();
  318. return action->getId();
  319. }
  320. UndoAction* UndoManager::getUndoAction(S32 index)
  321. {
  322. if ((index < getUndoCount()) && (index >= 0))
  323. return mUndoStack[index];
  324. return NULL;
  325. }
  326. ConsoleMethod(UndoManager, getRedoCount, S32, 2, 2, "")
  327. {
  328. return object->getRedoCount();
  329. }
  330. S32 UndoManager::getRedoCount()
  331. {
  332. return mRedoStack.size();
  333. }
  334. ConsoleMethod(UndoManager, getRedoName, const char*, 3, 3, "(index)")
  335. {
  336. return object->getRedoName(dAtoi(argv[2]));
  337. }
  338. const char* UndoManager::getRedoName(S32 index)
  339. {
  340. if ((index < getRedoCount()) && (index >= 0))
  341. return mRedoStack[getRedoCount() - index - 1]->mActionName;
  342. return NULL;
  343. }
  344. ConsoleMethod(UndoManager, getRedoAction, S32, 3, 3, "(index)")
  345. {
  346. UndoAction * action = object->getRedoAction(dAtoi(argv[2]));
  347. if ( !action )
  348. return -1;
  349. if ( !action->isProperlyAdded() )
  350. action->registerObject();
  351. return action->getId();
  352. }
  353. UndoAction* UndoManager::getRedoAction(S32 index)
  354. {
  355. if ((index < getRedoCount()) && (index >= 0))
  356. return mRedoStack[index];
  357. return NULL;
  358. }
  359. //-----------------------------------------------------------------------------
  360. const char* UndoManager::getNextUndoName()
  361. {
  362. if(mUndoStack.size() < 1)
  363. return NULL;
  364. UndoAction *act = mUndoStack.last();
  365. return (*act).mActionName;
  366. }
  367. //-----------------------------------------------------------------------------
  368. const char* UndoManager::getNextRedoName()
  369. {
  370. if(mRedoStack.size() < 1)
  371. return NULL;
  372. UndoAction *act = mRedoStack.last();
  373. return (*act).mActionName;
  374. }
  375. //-----------------------------------------------------------------------------
  376. void UndoManager::addAction(UndoAction* action)
  377. {
  378. // If we are assembling a compound, redirect the action to it
  379. // and don't modify our current undo/redo state.
  380. if( mCompoundStack.size() )
  381. {
  382. mCompoundStack.last()->addAction( action );
  383. return;
  384. }
  385. // clear the redo stack
  386. clearStack(mRedoStack);
  387. // push the incoming action onto the stack, move old data off the end if necessary.
  388. mUndoStack.push_back(action);
  389. if(mUndoStack.size() > mNumLevels)
  390. mUndoStack.pop_front();
  391. Con::executef(this, "onAddUndo");
  392. }
  393. //-----------------------------------------------------------------------------
  394. CompoundUndoAction* UndoManager::pushCompound( const String& name )
  395. {
  396. mCompoundStack.push_back( new CompoundUndoAction( name ) );
  397. return mCompoundStack.last();
  398. }
  399. //-----------------------------------------------------------------------------
  400. void UndoManager::popCompound( bool discard )
  401. {
  402. AssertFatal( getCompoundStackDepth() > 0, "UndoManager::popCompound - no compound on stack!" );
  403. CompoundUndoAction* undo = mCompoundStack.last();
  404. mCompoundStack.pop_back();
  405. if( discard || undo->getNumChildren() == 0 )
  406. {
  407. if( undo->isProperlyAdded() )
  408. undo->deleteObject();
  409. else
  410. delete undo;
  411. }
  412. else
  413. addAction( undo );
  414. }
  415. //-----------------------------------------------------------------------------
  416. ConsoleMethod(UndoAction, addToManager, void, 2, 3, "action.addToManager([undoManager])")
  417. {
  418. UndoManager *theMan = NULL;
  419. if(argc == 3)
  420. {
  421. SimObject *obj = Sim::findObject(argv[2]);
  422. if(obj)
  423. theMan = dynamic_cast<UndoManager*> (obj);
  424. }
  425. object->addToManager(theMan);
  426. }
  427. //-----------------------------------------------------------------------------
  428. ConsoleMethod( UndoAction, undo, void, 2, 2, "() - Undo action contained in undo." )
  429. {
  430. object->undo();
  431. }
  432. //-----------------------------------------------------------------------------
  433. ConsoleMethod( UndoAction, redo, void, 2, 2, "() - Reo action contained in undo." )
  434. {
  435. object->redo();
  436. }
  437. //-----------------------------------------------------------------------------
  438. ConsoleMethod(UndoManager, undo, void, 2, 2, "UndoManager.undo();")
  439. {
  440. object->undo();
  441. }
  442. //-----------------------------------------------------------------------------
  443. ConsoleMethod(UndoManager, redo, void, 2, 2, "UndoManager.redo();")
  444. {
  445. object->redo();
  446. }
  447. //-----------------------------------------------------------------------------
  448. ConsoleMethod(UndoManager, getNextUndoName, const char *, 2, 2, "UndoManager.getNextUndoName();")
  449. {
  450. const char *name = object->getNextUndoName();
  451. if(!name)
  452. return NULL;
  453. char *ret = Con::getReturnBuffer(dStrlen(name) + 1);
  454. dStrcpy(ret, name);
  455. return ret;
  456. }
  457. //-----------------------------------------------------------------------------
  458. ConsoleMethod(UndoManager, getNextRedoName, const char *, 2, 2, "UndoManager.getNextRedoName();")
  459. {
  460. const char *name = object->getNextRedoName();
  461. if(!name)
  462. return NULL;
  463. char *ret = Con::getReturnBuffer(dStrlen(name) + 1);
  464. dStrcpy(ret, name);
  465. return ret;
  466. }
  467. //-----------------------------------------------------------------------------
  468. ConsoleMethod( UndoManager, pushCompound, const char*, 2, 3, "( string name=\"\" ) - Push a CompoundUndoAction onto the compound stack for assembly." )
  469. {
  470. String name;
  471. if( argc > 2 )
  472. name = argv[ 2 ];
  473. CompoundUndoAction* action = object->pushCompound( name );
  474. if( !action )
  475. return "";
  476. if( !action->isProperlyAdded() )
  477. action->registerObject();
  478. return action->getIdString();
  479. }
  480. //-----------------------------------------------------------------------------
  481. ConsoleMethod( UndoManager, popCompound, void, 2, 3, "( bool discard=false ) - Pop the current CompoundUndoAction off the stack." )
  482. {
  483. if( !object->getCompoundStackDepth() )
  484. {
  485. Con::errorf( "%s::popCompound - no compound on stack", argv[ 0 ] );
  486. return;
  487. }
  488. bool discard = false;
  489. if( argc > 2 )
  490. discard = dAtob( argv[ 2 ] );
  491. object->popCompound( discard );
  492. }