Warning, /multimedia/stopmotion/src/domain/undo/README.md is written in an unsupported language. File is not indexed.
0001 The Undo System 0002 =============== 0003 0004 This document is to help developers understand the architecture 0005 of Stopmotion and write their own commands. 0006 0007 Stopmotion's undo system is designed to allow not only Undo and Redo 0008 of any and all operations, but to write those operations to a file 0009 so that any crash can be recovered from. The idea is that the system 0010 should be so reliable that Stopmotion does not need a "do you want 0011 to save before exiting?" dialog; the app can simply close and 0012 recover to exactly the same point by reading the undo history log 0013 next time it opens. The user therefore need not bother to name her 0014 project until she wants to change to a different project later, and 0015 the crash recovery system gets a constant workout as it is used for 0016 every app startup. 0017 0018 Stopmotion can do this because each command is invoked by 0019 specifying its name and parameters (rather than by calling a 0020 method or passing an object, for example). In normal operation this 0021 specification is written to a file (the 'undo history log'). An undo 0022 or redo is also written to this log. When recovering, the 0023 specifications are read from this file in order and so the same 0024 sequence of operations is reconstructed and applied to the project. 0025 This results in a project in exactly the same state as when the app 0026 was closed (or crashed!), even down to having the same commands on 0027 its undo and redo stacks. 0028 0029 In order to achieve this, the app has some tricky code in 0030 `executor.cpp`, but the most important factor in making it work 0031 is that every possible change to the project is performed through a 0032 Command object that adheres to strict rules. 0033 0034 No changes to the project except through commands 0035 ------------------------------------------------- 0036 0037 There must be no direct change to the project. All changes are 0038 mediated through the `Animation` class, which always calls the 0039 `ConcreteExecutor` object in order to achieve real work. The 0040 `ConcreteExecutor` knows which command factory to call based on the 0041 name of the command it needs to produce. This allows it to work the 0042 same way whether the instruction came from the `Animation` class or 0043 from the undo history log. 0044 0045 There can be no command that does not allow undo; there can be no 0046 "Are you sure you want to do this? It cannot be undone" dialog. 0047 0048 All commands have a factory that produces them 0049 ---------------------------------------------- 0050 0051 `addallcommands.cpp` contains a function 0052 `makeAnimationCommandExecutor` that adds each factory (with its 0053 command's name) to the `ConcreteExecutor`. This enables a line 0054 of text from the undo history log to construct the correct command. 0055 Each command factory derives from `CommandFactory`. Factories must 0056 produce commands from parameters read from a `Parameters` object. 0057 This parameters object either came from the `Animation` class or from 0058 the recovery system. Normally this is a standard parameters object, 0059 but some derive their own to help the `Animation` class avoid memory 0060 leaks-- do not worry about this, it is almost never a good idea. 0061 0062 All commands produce their own inverse 0063 -------------------------------------- 0064 0065 Commands derive from the `Command` class. `Command` only has two 0066 methods. The first, `accept`, is not very interesting. It is used to 0067 tell a `FileNameVisitor` which files are owned by this command. It is 0068 used for testing. 0069 0070 The second, `execute`, is the heart and soul of the undo system. This 0071 method performs three actions: 0072 * Perform the command (altering the project) 0073 * Construct an inverse of the command 0074 * Delete itself 0075 And it must do these operations carefully. There must be no memory 0076 allocation or other possibility of exceptions being thrown from the 0077 moment the project starts to be altered until the method ends. This 0078 means constructing the inverse and reserving any memory that will be 0079 required before changing the project in any way. It also means that 0080 if a command cannot be completed in one step without the possibility 0081 of leaving the project in a half-way state, the developer should 0082 re-think the design, possibly it should be broken up into smaller 0083 commands that can alter the project without risking leaving it in a 0084 half-way state. 0085 0086 Many commands come in pairs. A 'remove' command takes something out 0087 of the project and stores it in an 'add' command (its inverse). The 0088 'add' command adds it to the project and stores its position in a 0089 'remove' command. 0090 0091 Everything is fiercely tested 0092 ----------------------------- 0093 0094 Any command that fulfils the above criteria is automatically 0095 thoroughly tested. The function `testUndo` in `testundo.cpp` 0096 creates random commands with random parameters and tests that these 0097 strings of commands obey all the rules required to make the undo 0098 system bulletproof. `testUndo` is called twice: First it is called 0099 with a simple set of commands for manipulating a string. This tests 0100 that the undo system itself works. Secondly it is called with the 0101 commands Stopmotion uses for its own projects. This tests that all 0102 the commands work properly. 0103 0104 Run the tests by typing, at the project root: 0105 0106 make test 0107 0108 `testUndo` works by constructing a tree of projects; each leaf of 0109 the tree is reached by performing some set of actions. A set of 0110 actions might be running a string of commands, or running a string 0111 of commands where one memory access fails, or re-running commands 0112 from the undo history log, or many other things. Certain pairs of 0113 leaves of this tree should result in the same project; for example 0114 running commands A, then clearing the undo history log, then 0115 running commands B should give the same result as running commands 0116 A then replaying the history log. Similarly, running commands C then 0117 undoing them all then redoing them all should give the same result 0118 as just running commands C. 0119 0120 I hope this helps others understand what is going on here! -Tim