I cannot figure out what is the most convenient way to add undo and redo functions to (custom) objects, classes, etc…
At the moment I am looking at xml to store the status of each control, but this approach seems very inefficient, so
I am looking for suggestions…How do you do this in fairly complex apps?
That does sound inefficient (to use XML for each change). You could achieve this multiple ways:
- Write a ‘Clone’ method for ‘undoable’ classes. Create an array of clones that are appending to on a change of one or more values.
- Design your class to store the different versions of the value, for example a piece of text. Just append to a list on changing the text.
- Not so sure if this is a good idea but I suppose it depends on what you are trying to achieve but you could list each action then work out how to revert back from the action.
As far as I know, there is not really a good way of taking shortcuts for any kind of redo/undo or cloning class type functionality.
Hope this helps
I would recommend against this, especially for classes which deal with large amounts of data. The memory footprint will be huge and it will likely make your app slow.
There’s two ways in which we’ve done Undo.
#1 Is to make each and every class have getters and setters, when a new value is set it reports to that document’s undo engine it’s ID, the property ID and the previous value.
#2 Is in the interface, we have a Undo object. When a control is changed, it checks the objects previous value, if it’s different it then reports an ID to the Undo engine with the previous value.
Internally I simply use pairs storing the ID and value.
For the file formats I’ve used binary formats (quickest) and JSON (slowest). I prefer JSON while debugging because it’s easier to check the data on the disk. However the binary formats are much quicker to read and write.
A lot depends on the app.
The classic ‘many undo level’ approach is to have a handler method that is called for every type of change.
The method records what was done into a stack, and then does it.
Typical actions for text based app might be
‘Select from position 10 to 300’
‘Cut this text and copy to clipboard’
‘Move cursor to 0,0’
and so on
(These actions are in effect what you use when you ‘remote control’ apps like Office using OLE automation)
These are small instructions, inexpensive to store, and undo is basically
‘start with the empty document or the last opened document, then replay all the instructions up to the one before last’
(Why replay from start? Not all instructions are easily undone in reverse… for example ‘replace all instances of DOLLAR by EURO’ … you would need to know where all the DOLLARS were before the call was made)
Graphic applications can work that way, or might simply keep saving copies of the image at key points in the temp folder, and undo would be reload one of them.
I’ve recently implemented an undo system that uses a stack of xmldocuments to store state.
AFTER an action has occurred, I make a snapshot.
Undo is a case of popping an xmldocument from a stack, and restoring state from there.
Pushing onto the stack is done after checking the size of the stack: if it gets to my maximum size, I delete the oldest item.
Thanks for the replies. I still have a doubt. Let’s consider my scenario:
- I have an application which can have several tabs, connected to a PagePanel
- Each page will contain one (of the many possible) custom ContainerControl I designed
- Each ContainerControl will include several controls
In my case I will have more than one instance per Class/ContainerControl (for example in two different tabs I could have two instances of the same ContainerControl). How to deal with it when undoing? How to save the different instances?
I was thinking of a Dictionary (with tab index as key and the ContainerControl as value) but I cannot store different ContainerControl in the same Dictionary…
The memento pattern is used to implement a Undo-Redo mechanism in a OOP language like Xojo.
How far back do you want to undo?
If the container controls hold text areas and you are on a Mac, I seem to recall that the OS will handle undo on a control by control basis.
If the container controls hold a set of controls which expose the fields of a class, do you want undo to undo all changes to that instance of the class, or just changes in the field that has the focus?
If its the class, make the undo methodology a feature of the class.
So that each object is maintaining its own undo history.
If you want it field by field, you might subclass the individual controls
You would only do undo at the app level if the tabs and containers all form part of one document class.
@jim mckay has a module called “The Big Undo.” You should check it out. It contains all the classes and everything necessary to implement undo functions.
The Big Undo
I went with Jim’s “The Big Undo” last year and have used it for EVERYTHING ever since. The one or two issues I had early on (e.g., handling undo for radio button groups) were solved almost immediately by Jim with revisions to his software. Couldn’t ask for a better product for the job or a better customer support response from Jim (piDog Software) … and no, I don’t work there or know Jim personally … just want to pass on what turned out to be a great solution for me.
Wow… I just watched a demo of The Big Undo.
It really looks promising. I hope it might work for a project I am working on, using a canvas with drag-able elements, much like a video-editor’s timeline.
An undo function is rather essential in most software, I think. Not for us, of course… as we tend not to make any mistakes. But you know the software users