undo.cpp 16 KB

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