undo/redo Topic is solved

This forum can be used to talk about general design strategies, new ideas and questions in general related to wxWidgets. If you feel your questions doesn't fit anywhere, put it here.
Post Reply
Vexator
I live to help wx-kind
I live to help wx-kind
Posts: 187
Joined: Sun Jan 30, 2005 2:50 pm
Location: Heidelberg, Germany

undo/redo

Post by Vexator » Tue May 16, 2006 1:24 pm

i'd like to add undo/redo functionality to my app, but i have no idea how to. is there a general approach? which way do you support this feature in your app? thanks in advance!
Windows 7 Pro
Visual Studio 2010
wxWidgets 2.9.3

Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg » Tue May 16, 2006 2:11 pm

It depends how your data model is driven, and how many effort you would like to put in it. Multi level undo, or just one state ..

There is the Command Pattern which basically comes down to that every change to your data model is a "command" which is an object. The data model executes the command, and stores a previous state of the data model that belongs to e.g. the RollBack method of the command. All commands are stored in a queue, and you remember the last e.g. 10 of them.

One thing you need to change is the paradigm of how GUI's drive the model. For example, adding or deleting objects to your object model are now interchangable. There should be no code in your GUI that actually creates the classes or deletes them .. The data model should do all of that, and e.g. use sigslot or notification callbacks to the GUI which acts like a dumb piece of application and simply adds or deletes the classes that are new or deleted to or from the respective GUI elements.

The hardest thing is the state store. You should device a way that the delta's from the object model are stored in a serialized version .. For example when you know some parts of the object are "changed" only these should be serialized. When other classes are "Added" in the next part, you should remove them again when the user presses undo.

There is no clear way how to do it, but the method above is the most used one. I wrote wxArchive which you might find useful for data serialization and state storage. Derive every undo / redo class from a base class that can indicate to the serializer if it is added or changed since last state store. There should be a "changed" method that enforces the state change serialization for you. Consistency is the key :-)

I hope this little lecture proves helpful :-)

- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb

User avatar
tierra
Site Admin
Site Admin
Posts: 1343
Joined: Sun Aug 29, 2004 7:14 pm
Location: Salt Lake City, Utah, USA
Contact:

Post by tierra » Tue May 16, 2006 2:16 pm

There is a wxCommandProcessor provided by the Document/View framework that handles all the fun stuff like enabling/disabling Undo/Redo toolbar buttons and menu items, renaming menu items to show the last command to be undone, etc...

http://wxwidgets.org/manuals/2.6.3/wx_d ... ndoverview

It may be possible to still use this from wxDocument without the rest of the doc/view framework, but if not, it still shouldn't be hard to come up with your own implementation. See the doc/view framework docs for more details:

http://wxwidgets.org/manuals/2.6.3/wx_d ... ewoverview

phlox81
wxWorld Domination!
wxWorld Domination!
Posts: 1387
Joined: Thu Aug 18, 2005 7:49 pm
Location: Germany
Contact:

Post by phlox81 » Tue May 16, 2006 3:10 pm

THeres although another way:
You must have a single documentroot class in your app.
Then, at every change, you got to copy this _before_ you change the
document, and add it to some undo list. If you now undo, you simply
set the actual document to the old one, and add the other one to the
redo list.

phlox

Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg » Tue May 16, 2006 7:56 pm

That works only if the GUI app holds no pointers to existing classes. If you duplicate the whole data model it can also get vert slow and memory consuming. Keeping deltas is not so difficult, simply store only the classes you changed. Your redo logicmust however be smart enough to know what to put back and notify all listeners

- For adding -> Signal the GUI a class is added, let the GUI decide if it needs to be displayed or not (keeping pointers is safe)
- For updating -> Signal the GUI that an existing class is chaged
- For deletion -> The GUI must release all pointers it utilizes of the classes being deleted.

When an undo is done on a delete, it becomes another add action. Some extra data might need to be stored like position of the original element in the GUI, e.g. with sorting algoritms. But I think that is simply another data model that utilizes the first one. The GUI should really play dump and process only those three actions signaled from the data classes.

- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb

phlox81
wxWorld Domination!
wxWorld Domination!
Posts: 1387
Joined: Thu Aug 18, 2005 7:49 pm
Location: Germany
Contact:

Post by phlox81 » Tue May 16, 2006 8:50 pm

yeah, if you hold any pointers, thats gonna be a wild ride *g*
You could load the whole document again, to overcome this problem,
but than you got other Problems :D

The whole Commandthing is a nice Idea, but I think it is pretty complicated
and much work of implementing it, so that every Command(Action) has
its counterpart.
Has every Actionclass than an undo and redo method?

phlox

User avatar
tierra
Site Admin
Site Admin
Posts: 1343
Joined: Sun Aug 29, 2004 7:14 pm
Location: Salt Lake City, Utah, USA
Contact:

Post by tierra » Wed May 17, 2006 4:02 am

phlox81 wrote:The whole Commandthing is a nice Idea, but I think it is pretty complicated
and much work of implementing it, so that every Command(Action) has
its counterpart.
Not really, wxCommand and wxCommandProcessor take care of all the common functionality of undo/redo for you, and all you have to do is derive new wxCommand(s) with Undo() and Redo() methods overwritten (so yes phlox81, to answer your question). With lots of use, some of your commands that do a lot of the same stuff can be combined into one wxCommand class that in the end looks like 5 or 6 different ones just with different options. Also, this is the best way to go about it if you eventually want to serialize your document change history (so you can Undo even after saving, closing, and re-opening) since you can just add another base command class that defines a virtual serialization function from there without refactoring everything. The method of Undo/Redo you end up using is still up to you, so you can use the suggestions already provided above, or go a simpler, more memory efficient way of just saving off the action to be taken, and reversing it on Undo.

I work on a project over 2 years old with over 100,000 lines of code that has been using wxCommand/wxCommandProcessor with the doc/view framework very successfully for the last year and a half or so. We've only needed to derive maybe 7 or 8 wxCommands that handle every action that can be taken against the base document data (we don't use the command processor for layout/view changes with a couple exceptions).

Post Reply