Daniel Sapoundjiev on
  Undo Redo in software applications

Undo Redo in software applications

Life is irreversible

About

The idea of undo redo is after making a change to be possible to go back to the state before the change. And after undo to be possible to remake the change again.

Tries and mistakes

During the process of creation the creator makes a lot of activities before getting the final product. And somewhere there the creator makes mistakes. Some of them are noticed immediately and other at later stage. Those noticed immediately can be correct by making a step back. Those noticed later can be recovered by returning to the point of the mistake and remaking again the things after it.

If every step depends on the previous then all can be fixed by fixing the mistake at the given step. But this will be explained in another research.

It’s not always about mistakes. Sometimes creator just tries different things to see if something good will appear. Trying something, then returns back then trying something else and so on.

Saving whole model or just the changes

I can think of two ways to keep the state. After every change keep the whole new information about the model. The other is after every change to keep only the change that is made. The first way is acceptable only for small data. For bigger models it will take a lot of memory to handle that.

ChangeObject

For every change of something in the model we can create a ChangeObject that will hold the information about the change

One step many changes.

When user makes an action it often changes a few values or objects in the model. When undo is performed it must undo the whole action. However it will be better to have a few sub-actions for every particular change.

Many changes at time

Some times a lot of objects are changed at a time. So we need to create ...

Not everything is to be undone.

It’s a good question, what needs to be undone and what not. There are some actions that needs pretty more attention. These things are not very good candidates for undo-redo. Also these things are some how away from the work area where most of the changes happen. For example if we take a look at MS Word. Changing the default language for the document will not affect the words that are written till that moment. This happen rarely. But typing is what we do most of the time.

Usually most of the work is done in one place. It’s some kind of work area. Things that we do there are good candidates for undo-redo.

In and out of model

One object can leave outside the model. Usually every object is outside the model first. It is created and then it is initialized. And after that it is appended to the model. Even objects of same class can live their whole life outside the model. That is when they are used for some other purposes. Regarding the undo redo we don’t care about their life outside the model. So when they are appended to the model we start to monitor them for changes and so on. If they leave the model we don’t care about them too.

How to undo lists in the model

Most of the models contain objects in lists. So, its important to have good strategy for undoing these list operations.

We can add or remove element in the list. Also many elements can be added or deleted at time.

When we add element in the list we can record the element, the list, and the index if needed. So, when undo is performed element will be removed from the list. When redo is performed the element will be put back in the list.

When element is removed from the list, the element, the list and an index is stored in UndoRedoChange. When an undo is performed the element is added to the list at the specified position. When redo is performed element will be removed again.

This is the same for any kind of list and object, so it can be used for all lists in the model

But how is better. To listen for change in the list or just to know it when you make the change in the user step. I still beleive for complex models it will be difficult to know all the changes in the model. They all depends on some business logic. So, attaching and listening to the lists will help us not to track all possible changes. But when listening we have to know if the model is loading at the moment or is in undo redo operation at the moment.

How to undo change in object reference in the model

The resources in one model are objects and lists. An object can contain other objects or lists. So, we will need to have undo/redo when contained object changes. Usually the object contains reference to other object like property. So, when this property changes undo/redo change will be registered. Of course when loading the model we don’t need this to be registered. Also when we are making the undo/redo also don’t have to be registered.

We can make one ObjectValueChange which will contain the change for every object. It will contain the main Object, the name of the Property that will be changed, the old value and the new value. It uses reflection to store the new or old value, by the given Property name. So, this way we have only one class for all changes of objects.

Undo replacement of item in list

In model we have lists of objects. We can append and remove items from these lists. But also we can replace existing item in the list with other one. In this case we have a change in the model and it has to be available for undo. When lists do not contain duplicate items we can store the list object, the old value, and the new value.

What developer needs to do to have it working

Developer needs a fast way for implementing the Undo Redo in application. Less steps, less work.

How much work is with this approach

The UndoRedo object, the user action object, the UndoRedoChange class is one time job. They will work universally and no code needs to be written there for different applications. Descendent of change object about the lists undo redo is also one time job. It will work for the lists in all projects. The same is for ObjectValueChange. The base listner class will have logic to attach to lists and create the list change object. So this is one time job too.

1. Add UndoRedo object for a model.

2. Add menus and buttons for undo redo and write code for calling UndoRedo methods.

3. Write code for attaching to the model of the listener object.

4. Write code for creating the change object for specific change.

5. Write code for change objects (if needed)

and we have it working

Back to main menu