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