undo.cpp 16 KB

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