Give me some thoughts how to implement undo/redo functionality - like we have in text editors. What algorithms should I use and what I may read. thanks.
If “UNDO” string is encountered, pop the top element from Undo stack and push it to Redo stack. If “REDO” string is encountered, pop the top element of Redo stack and push it into the Undo stack. If “READ” string is encountered, print all the elements of the Undo stack in reverse order.
Hiero supports undo/redo. Most Python commands which affect the underlying state of an object create an undo/redo action which can then be undone or redone through Python or through the Edit > Undo and Redo menu items.
To undo an action, press Ctrl + Z. To redo an undone action, press Ctrl + Y. The Undo and Redo features let you remove or repeat single or multiple typing actions, but all actions must be undone or redone in the order you did or undid them – you can't skip actions.
I know about two major divisions of the types of undo's
For text editors, generating the state this way is not too computation intensive but for programs like Adobe Photoshop, it might be too computationally intensive or just plain impossible. For example - for a Blur action, you will specify a de-Blur action, but that can never get you to the original state because the data is already lost. So, depending on the situation - possibility of a logical inverse action and the feasibility of it, you need to choose between these two broad categories, and then implement them the way you want. Ofcourse, it is possible to have a hybrid strategy that works for you.
Also, sometimes, like in Gmail, a time limited undo is possible because the action (sending the mail) is never done in the first place. So, you are not "undo"ing there, you are just "not doing" the action itself.
I have written two text editors from scratch, and they both employ a very primitive form of undo/redo functionality. By "primitive", I mean that the functionality was very easy to implement, but that it is uneconomical in very large files (say >> 10 MB). However, the system is very flexible; for instance, it supports unlimited levels of undo.
Basically, I define a structure like
type TUndoDataItem = record text: /array of/ string; selBegin: integer; selEnd: integer; scrollPos: TPoint; end;
and then define an array
var UndoData: array of TUndoDataItem;
Then each member of this array specifies a saved state of the text. Now, on each edit of the text (character key down, backspace down, delete key down, cut/paste, selection moved by mouse etc.), I (re)start a timer of (say) one second. When triggered, the timer saves the current state as a new member of the UndoData
array.
On undo (Ctrl+Z), I restore the editor to the state UndoData[UndoLevel - 1]
and decrease the UndoLevel
by one. By default, UndoLevel
is equal to the index of the last member of the UndoData
array. On redo (Ctrl+Y or Shift+Ctrl+Z), I restore the editor to the state UndoData[UndoLevel + 1]
and increase the UndoLevel
by one. Of course, if the edit timer is triggered when UndoLevel
is not equal to the length (minus one) of the UndoData
array, I clear all items of this array after UndoLevel
, as is common on the Microsoft Windows platform (but Emacs is better, if I recall correctly -- the disadvantage of the Microsoft Windows approach is that, if you undo a lot of changes and then accidentally edit the buffer, the previous content (that was undid) is permanently lost). You might want to skip this reduction of the array.
In a different type of program, for instance, an image editor, the same technique can be applied, but, of course, with a completely different UndoDataItem
structure. A more advanced approach, which does not require as much memory, is to save only the changes between the undo levels (that is, instead of saving "alpha\nbeta\gamma" and "alpha\nbeta\ngamma\ndelta", you could save "alpha\nbeta\ngamma" and "ADD \ndelta", if you see what I mean). In very large files where each change is small compared to the file size, this will greatly decrease the memory usage of the undo data, but it is trickier to implement, and possibly more error-prone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With