How the UNDO and REDO feature in any TEXT EDITOR is implemented? [duplicate] - algorithm

Part of my project is to write a text editor that is used for typing some rules, compiling my application and running it. Writing compiler was end and release beta version. In the final version we must add undo and redo to the text editor. I use a file and save it periodically for the text editor. How to design undo and redo to my text editor? What is changed in the structure of persistent of file?

You can model your actions as commands, that you keep in two stacks. One for undo, another for redo. You can compose your commands to create more high-level commands, like when you want to undo the actions of a macro, for example; or if you want to group individual keystrokes of a single word, or phrase, in one action.
Each action in your editor (or a redo action) generates a new undo command that goes into the undo stack (and also clears the redo stack). Each undo action generates the corresponding redo command that goes into the redo stack.
You can also, as mentioned in the comments by derekerdmann, combine both undo and redo commands into one type of command, that knows how to undo and redo its action.

There are basically two good ways to go about it:
the "Command" design pattern
using only OO over immutable objects, where everything is just immutable objects made of immutable objects made themselves of immutable objects (this is less common but wonderfully elegant when done correctly)
The advantage of using OO over immutable objects over the naive command or the naive undo/redo is that you don't need to think much about it: no need to "undo" the effect of an action and no need to "replay" all the commands. All you need is a pointer to a huge list of immutable objects.
Because objects are immutable all the "states" can be incredibly lightweight because you can cache/reuse most objects in any state.
"OO over immutable objects" is a pure jewel. Probably not gonna become mainstream before another 10 years that said ; )
P.S: doing OO over immutable objects also amazingly simplifies concurrent programming.

If you don't want anything fancy, you can just add an UndoManager. Your Document will fire an UndoableEdit every time you add or remove text. To undo and redo each change, simply call those methods in UndoManager.
The downside of this is UndoManager adds a new edit each time the user types something in, so typing "apple" will leave you with 5 edits, undoable one at a time. For my text editor, I wrote a wrapper for edits that stores the time it was made in addition to text change and offset, as well as an UndoableEditListener that concatenates new edits to previous ones if there is only a short period of time between them (0.5 seconds works well for me).
This works well for general editting, but causes problems when a massive replace is done. If you had a document with 5000 instances of "apple" and you wanted to replace this with "orange", you'd end up with 5000 edits all storing "apple", "orange" and an offset. To lower the amount of memory used, I've treated this as a separate case to ordinary edits and am instead storing "apple", "orange" and an array of 5000 offsets. I haven't gotten around to applying this yet, but I know that it'll cause some headaches when multiple strings match the search condition (eg. case insensitive search, regex search).

Wow, what a conicidence - I have literally in the last hour implemented undo/redo in my WYSIWYG text editor:
The basic idea is to either save the entire contents of the text editor in an array, or the difference between the last edit.
Update this array at significant points, i.e. every few character (check the length of the content each keypress, if its more than say 20 characters different then make a save point). Also at changes in styling (if rich text), adding images (if it allows this), pasting text, etc. You also need a pointer(just an int variable) to point at which item in the array is the current state of the editor)
Make the array have a set length. Each time you add a save point, add it to the start of the array, and move all of the other data points down by one. (the last item in the array will be forgotten once you have so many save points)
When the user presses the undo button, check to see if the current contents of the editor are the same as the latest save (if they are not, then the user has made changes since the last save point, so save the current contents of the editor (so it can be redo-ed), make the editor equal to the last save point, and make the pointer variable = 1 (2nd item in array ). If they are they same, then no changes have been made since the last save point, so you need to undo to the point before that. To do this, increment the pointer value + 1, and make the contents of the editor = the value of pointer.
To redo simply decrease the pointer value by 1 and load the contents of the array (make sure to check if you have reached the end of the array).
If the user makes edits after undoing, then move the pointed value array cell up to cell 0, and move the rest up by the same amount (you dont want to redo to other stuff once they've made different edits).
One other major catch point - make sure you only add a save point if the contents of the text editor have actually changed (otherwise you get duplicate save points and it will seem like undo is not doing anything to the user.
I can't help you with java specifics, but I'm happy to answer any other questions you have,
Nico

You can do it in two ways:
keep a list of editor states and a pointer in the list; undo moves the pointer back and restores the state there, redo moves forward instead, doing something throws away everything beyond the pointer and inserts the state as the new top element;
do not keep states, but actions, which requires that for every action you have a counteraction to undo the effects of that action
In my (diagram) editor, there are four levels of state changes:
action fragments: these are part of a larger action and not separately undoable or redoable
(e.g. moving the mouse)
actions: one or more action fragments that form a meaningful change which can be undone or redone,
but which are not reflected in the edited document as changed on disk
(e.g. selecting elements)
document changes: one or more actions that change the edited document as it would be saved to disk
(e.g. changing, adding or deleting elements)
document saves: the present state of the document is explicitly saved to disk - at this point my editor throws away the undo history, so you can't undo past a save

This is a job for the command pattern.

Here is a snippet that shows how SWT supports Undo/Redo operations. Take it as practical example (or use it directly, if your editor is based on SWT):
SWT Undo Redo

Read a book Design Patterns: Elements of Reusable Object-Oriented Software. As far as I remember, there is a pretty good example.

Related

Saving un-do/re-do where there is no inverse action

Let's say I am in a spreadsheet application and do three actions:
(Open a new spreadsheet.)
Type "Hello" in cell A1.
Make cell A1 bold.
Type in "yellow" in cell A2.
In order to un-do all operations I could store something like:
UNDO_STACK = [
{ActiveCell: 'A1', Text: ''}, # undo for "hello"
{ActiveCell: 'A1', Bold: false}, # undo for bold
{ActiveCell: 'A2', Text: ''}, # undo for "yellow"
]
Of course there can be tons more actions and the above data structure is quite simplified. However, my questions is how a complex action such as "Replace 'e' with 'l'" would work? I couldn't just save the 'action' because it is not exactly inversible, as much of the original data is lost. For something like a replace operation does an application like Excel or Google sheets need to save every replace operation that has been made? What if it's a very large file, such as a 500MB file in Excel, couldn't the undo operation itself take up ~10MB or more if storing the placement of every undo operation (for example, if half of the cell values are NULL and I want to replace NULL with '') ? How could the replace operation be saved in a memory-efficient way?
Most editing actions are non-invertible. For example, your first action of changing cell A1 to "hello" is non-invertible since it loses the previous text in that cell. The solution is as you wrote in your question: store the additional information required to restore the original state. In this case, the contents of cell A1 before it was changed were "" (the empty string), so storing the empty string in the "undo" object allows the old state to be restored.
Likewise, a bulk search-and-replace operation is non-invertible, so it also needs sufficient data to restore the old state. A list of cells, indices and the deleted substrings would be sufficient. This object could contain quite a lot of data if the spreadsheet is large and many cells are affected; in principle you could compress it (e.g. using an algorithm like gzip), but I doubt many applications do this. It's more likely you would just store the "undo" object as is. Your example of needing 10MB of memory for a bulk edit on a large spreadsheet is not unrealistic, but also 10MB is not a lot of memory these days.
If an "undo" object would require too much memory, then you can always just choose not to store it, in which case that action (and any before it) can't be undone. In some circumstances (e.g. applying a transformation to the whole of a very large image), the information needed to undo the action is a complete copy of the original image, so you might instead get a warning that the operation will use a lot of memory and can't be undone. If you're implementing this, make sure to clear the whole undo stack when this happens.
Typically this is handled by creating a class or an object that contains all of the possible states. In your example, your properties for that cell would be String and Bold (Bool). There would be some function that detects a change to that particular object and then effectively saves it to a file or memory location that holds the data until it's needed. Interestingly different pieces of software use these states differently as you might expect. For example, Excel will store undo actions until the workbook becomes saved, but if Excel crashes, it will attempt to retain those actions when you recover the workbook. For reference, an excel sheet with approximately 80,000 cells will use about 20Mb of data if that data is only string values. It's essential to keep in mind when working with efficiency that there you could have a Save or a Generate.
Save: The save method would do precisely as you would expect. It's going to keep every change to your information in memory with whatever the designer is asking. If you want to undo an action, it merely reverts to the previous save, etc..
Generate: This one is a little different in that it's a known state change and can be calculated for the undo action. You can think of this type as a computed property to an object. Some of these generated undo actions are apparent, for example, a bold or un-bold action where there is a true/false. Other causes of a generated work might be more challenging to identify, such as a rotation of an image. Generating would be the most efficient way to handle the replacing of NULL in the most efficient manner. Your function would still need to save some value for your ) but one character in memory is better than hundreds or thousands of characters in memory.
All in all, I think the more generatable undo actions you use, the more efficient it will be. Almost anything, barring static data, has an inverse operation that can be computed, thus saving memory. A copy/paste of a range of cells from cell A1 to A1:Z26 could be stored as a simple copy and paste using that exact information, Saving A1 and then merely copying that to A1: Z26 resulting in a dramatic performance increase over saving the entirety of A1:Z26 as an action.
I encourage you to check out this article, it contains great information that will help you understand better: https://mynameismjp.wordpress.com/2008/12/19/undo-and-redo/

Which one is the acceptable convention for undo / redo behavior?

Suppose this is a mathematical application that manipulates waveforms. User opens a waveform file, and edits it.
Now user amplifies waveform using the application toolbox. Amplification may take a long time. Then they undo it. And then they redo it again.
For redo, how should the application behave?
Replace result of amplification that was performed before and was held in memory internally by the application.
Re-run the time-consuming amplification procedure again?
This problem expands to every functionality.
Thanks :-)
Undo-redo actions are typically implemented using a stack of commands and the position of the last executed command. After an undo, you move that position one step backward without removing the last command of the stack (now one past the last executed command).
Whether you store data along with your commands is up to you. However, in the situation you describe, for the undo to work, you need to either reverse the last command perfectly (and you may not be able to), or keep the previous state in memory. So keeping the next state, after the amplification, should not cost you anything in this particular case. However, you do need to properly discard it if the user does not redo that command.
As #eddiewould pointed out in a comment, a command can be inherently destructive (so unreversible) and allowing to undo it (by keeping the previous state) can be unfeasable, for example because of memory restrictions. In such a case, you should inform the user beforehand that going back will be impossible.

What's the point of Incremental Find in Sublime Text?

At the end of this tutorial: https://www.youtube.com/watch?v=ICxMZGLKEbU the author makes it look like the Incremental Find (CTRL+I) is somehow a quicker way to get one's cursor from anywhere in the edited document to some specified place than when using the Regular Find (CTR+F), but I fail to see any difference in the number of steps required. You hit the keys, type a word to which you want to get to, and then either press ENTER (Incremental) or ESCAPE (Regular).
Also, I don't quite see why it's called incremental.
There is a slight difference in between the two functions. With Find (CtrlF), you type in what you're looking for, and each time you hit Enter the next occurrence of the pattern is shown, but it is not a selection in that you can begin typing right away - you need to hit Esc first, and the highlighting is changed to a selection.
With Incremental Find (CtrlI), on the other hand, you start typing in your pattern, and when you hit Enter, the Find highlighting is turned into a selection, the dialog box disappears, and you are immediately able to begin editing the selection. This saves a grand total of one keystroke over the Find method, but has the disadvantage that, unless you hit F3 (Find Next), you are only editing the first instance of the pattern found.
It's kind of a subtle difference, and each one can be used in different situations. Play around with them both and see how each fits into your workflow.
The only functional difference (other than using different keys) is that with incremental search you have the option of either going back to the original (pre-search) cursor position by hitting ESC or staying on the found result by hitting RETURN... whereas the full Find function does not give you the ability to go back to the original position (although you can easily do so with the Goto > Jump Back command after exiting your search).
So in the one very limited use-case of "I want to save 1 keystroke in the event that I want to go back to my original cursor position after searching and seeing what I found but not changing it at all", incremental search has the normal find functionality beat. However, to me this is a detriment because whenever I try incremental search, I wind up hitting that ESCAPE key by accident (because my muscle memory is so used to it from the normal Find functionality) and having to do the search over again.
Hence, practically speaking I think it's best to ignore the existence of Incremental Search. Why bother trying to teach your fingers a separate sequence of keyboard commands and an ever-so-slightly different mental model of how it works (but with no practical difference other than saving 1 keystroke in an edge-case at the expense of triggering it accidentally more often than you want it to actually happen on purpose)?
Historically speaking, I think incremental search is a holdover from older text editors that didn't have search functionality as well-done (IMO) as Sublime. (Actually I think Sublime v2's search interface didn't work as smoothly as v3 does, so maybe it was more helpful back in the old days of Sublime as well?)
The behavior of Ctrl+F copies the behavior of the Find dialog as implemented in the majority of mainstream software. The Incremental search copies Emacs' Incremental search functionality.
The biggest advantage of incremental search is, it shortens the search operation in most cases. Say you want to find "shakespeare". With regular search you must type the whole 11 letters, while with incremental you will most possibly get to the word after typing the first few letters.

Implementation of Unsaved Changes Detection

It seems like three ways to approach detecting unsaved changes in a text/image/data file might be to:
Update a boolean flag every time the user makes a change or saves, which would result in a lot of unnecessary updates.
Keep a cached copy of the original file and diff the two every time a save operation needs to be checked.
Keep a stack of all past operations and push/pop operations as needed, resulting in a lot of extra memory usage.
In general, how do commercial applications detect whether unsaved changes exist and what are the advantages/disadvantages of each approach? I ran into this issue while writing a custom application that has special saving behavior and would like to know if there is a known best practice.
As long as you need an undo/redo system, you need that stack of past operations. To detect in wich state the document is, an item of the stack is set to be the 'saved state'. Current stack node is not that item, the document is changed.
You can see an example of this in Qt QUndoStack( http://doc.qt.nokia.com/stable/qundostack.html ) and its isClean() and setClean()
For proposition 1, updating a boolean is not something problematic and take little time.
This depends on the features you want and the size/format of the files, I guess.
The first option is the simplest, and it gives you just what you want with minial overhead.
The second option has the advantage that you can detect when changes have been manually reverted, so that there is no real change after all (although that probably doesn't happen all too often). On the other hand it is much more costly to make a diff just to check if anything was modified. You probably don't want to do that everytime the user presses a key.
The third option gives the ability to provide an undo-history. You could limit the number of items in that history by grouping changes together that were made consecutively (without moving the cursor in between), or something like that.

How does differential execution work?

I've seen a few mentions of this on Stack Overflow, but staring at Wikipedia (the relevant page has since been deleted) and at an MFC dynamic dialog demo did nothing to enlighten me. Can someone please explain this? Learning a fundamentally different concept sounds nice.
Based on the answers: I think I'm getting a better feel for it. I guess I just didn't look at the source code carefully enough the first time. I have mixed feelings about differential execution at this point. On the one hand, it can make certain tasks considerably easier. On the other hand, getting it up and running (that is, setting it up in your language of choice) is not easy (I'm sure it would be if I understood it better)...though I guess the toolbox for it need only be made once, then expanded as necessary. I think in order to really understand it, I'll probably need to try implementing it in another language.
Gee, Brian, I wish I had seen your question sooner. Since it's pretty much my
"invention" (for better or worse), I might be able to help.
Inserted: The shortest possible
explanation I can make is that if
normal execution is like throwing a
ball in the air and catching it, then
differential execution is like
juggling.
#windfinder's explanation is different from mine, and that's OK. This technique is not easy to wrap one's head around, and it's taken me some 20 years (off and on) to find explanations that work. Let me give it another shot here:
What is it?
We all understand the simple idea of a computer stepping along through a program, taking conditional branches based on the input data, and doing things. (Assume we are dealing only with simple structured goto-less, return-less code.) That code contains sequences of statements, basic structured conditionals, simple loops, and subroutine calls. (Forget about functions returning values for now.)
Now imagine two computers executing that same code in lock-step with each other, and able to compare notes. Computer 1 runs with input data A, and Computer 2 runs with input data B. They run step-by-step side by side. If they come to a conditional statement like IF(test) .... ENDIF, and if they have a difference of opinion on whether the test is true, then the one who says the test if false skips to the ENDIF and waits around for its sister to catch up. (This is why the code is structured, so we know the sister will eventually get to the ENDIF.)
Since the two computers can talk to each other, they can compare notes and give a detailed explanation of how the two sets of input data, and execution histories, are different.
Of course, in differential execution (DE) it is done with one computer, simulating two.
NOW, suppose you only have one set of input data, but you want to see how it has changed from time 1 to time 2. Suppose the program you're executing is a serializer/deserializer. As you execute, you both serialize (write out) the current data and deserialize (read in) the past data (which was written the last time you did this). Now you can easily see what the differences are between what the data was last time, and what it is this time.
The file you are writing to, and the old file you are reading from, taken together constitute a queue or FIFO (first-in-first-out), but that's not a very deep concept.
What is it good for?
It occurred to me while I was working on a graphics project, where the user could construct little display-processor routines called "symbols" that could be assembled into larger routines to paint things like diagrams of pipes, tanks, valves, stuff like that. We wanted to have the diagrams be "dynamic" in the sense that they could incrementally update themselves without having to redraw the entire diagram. (The hardware was slow by today's standards.) I realized that (for example) a routine to draw a bar of a bar-chart could remember its old height and just incrementally update itself.
This sounds like OOP, doesn't it? However, rather than "make" an "object", I could take advantage of the predictability of the execution sequence of the diagram procedure. I could write the bar's height in a sequential byte-stream. Then to update the image, I could just run the procedure in a mode where it sequentially reads its old parameters while it writes the new parameters so as to be ready for the next update pass.
This seems stupidly obvious and would seem to break as soon as the procedure contains a conditional, because then the new stream and the old stream would get out of sync. But then it dawned on me that if they also serialized the boolean value of the conditional test, they could get back in sync.
It took a while to convince myself, and then to prove, that this would always work, provided a simple rule (the "erase mode rule") is followed.
The net result is that the user could design these "dynamic symbols" and assemble them into larger diagrams, without ever having to worry about how they would dynamically update, no matter how complex or structurally variable the display would be.
In those days, I did have to worry about interference between visual objects, so that erasing one would not damage others. However, now I use the technique with Windows controls, and I let Windows take care of rendering issues.
So what does it achieve? It means I can build a dialog by writing a procedure to paint the controls, and I do not have to worry about actually remembering the control objects or dealing with incrementally updating them, or making them appear/disappear/move as conditions warrant. The result is much smaller and simpler dialog source code, by about an order of magnitude, and things like dynamic layout or altering the number of controls or having arrays or grids of controls are trivial. In addition, a control such as an Edit field can be trivially bound to the application data it is editing, and it will always be provably correct, and I never have to deal with its events. Putting in an edit field for an application string variable is a one-line edit.
Why is it hard to understand?
What I have found hardest to explain is that it requires thinking differently about software. Programmers are so firmly wedded to the object-action view of software that they want to know what are the objects, what are the classes, how do they "build" the display, and how do they handle the events, that it takes a cherry bomb to blast them out of it. What I try to convey is that what really matters is what do you need to say? Imagine you are building a domain-specific language (DSL) where all you need to do is tell it "I want to edit variable A here, variable B there, and variable C down there" and it would magically take care of it for you. For example, in Win32 there is this "resource language" for defining dialogs. It is a perfectly good DSL, except it doesn't go far enough. It doesn't "live in" the main procedural language, or handle events for you, or contain loops/conditionals/subroutines. But it means well, and Dynamic Dialogs tries to finish the job.
So, the different mode of thinking is: to write a program, you first find (or invent) an appropriate DSL, and code as much of your program in that as possible. Let it deal with all the objects and actions that only exist for implementation's sake.
If you want to really understand differential execution and use it, there are a couple of tricky issues that can trip you up. I once coded it in Lisp macros, where these tricky bits could be handled for you, but in "normal" languages it requires some programmer discipline to avoid the pitfalls.
Sorry to be so long-winded. If I haven't made sense, I'd appreciate it if you'd point it out and I can try and fix it.
Added:
In Java Swing, there is an example program called TextInputDemo. It is a static dialog, taking 270 lines (not counting the list of 50 states). In Dynamic Dialogs (in MFC) it is about 60 lines:
#define NSTATE (sizeof(states)/sizeof(states[0]))
CString sStreet;
CString sCity;
int iState;
CString sZip;
CString sWholeAddress;
void SetAddress(){
CString sTemp = states[iState];
int len = sTemp.GetLength();
sWholeAddress.Format("%s\r\n%s %s %s", sStreet, sCity, sTemp.Mid(len-3, 2), sZip);
}
void ClearAddress(){
sWholeAddress = sStreet = sCity = sZip = "";
}
void CDDDemoDlg::deContentsTextInputDemo(){
int gy0 = P(gy);
P(www = Width()*2/3);
deStartHorizontal();
deStatic(100, 20, "Street Address:");
deEdit(www - 100, 20, &sStreet);
deEndHorizontal(20);
deStartHorizontal();
deStatic(100, 20, "City:");
deEdit(www - 100, 20, &sCity);
deEndHorizontal(20);
deStartHorizontal();
deStatic(100, 20, "State:");
deStatic(www - 100 - 20 - 20, 20, states[iState]);
if (deButton(20, 20, "<")){
iState = (iState+NSTATE - 1) % NSTATE;
DD_THROW;
}
if (deButton(20, 20, ">")){
iState = (iState+NSTATE + 1) % NSTATE;
DD_THROW;
}
deEndHorizontal(20);
deStartHorizontal();
deStatic(100, 20, "Zip:");
deEdit(www - 100, 20, &sZip);
deEndHorizontal(20);
deStartHorizontal();
P(gx += 100);
if (deButton((www-100)/2, 20, "Set Address")){
SetAddress();
DD_THROW;
}
if (deButton((www-100)/2, 20, "Clear Address")){
ClearAddress();
DD_THROW;
}
deEndHorizontal(20);
P((gx = www, gy = gy0));
deStatic(P(Width() - gx), 20*5, (sWholeAddress != "" ? sWholeAddress : "No address set."));
}
Added:
Here's example code to edit an array of hospital patients in about 40 lines of code. Lines 1-6 define the "database". Lines 10-23 define the overall contents of the UI. Lines 30-48 define the controls for editing a single patient's record. Note the form of the program takes almost no notice of events in time, as if all it had to do was create the display once. Then, if subjects are added or removed or other structural changes take place, it is simply re-executed, as if it were being re-created from scratch, except that DE causes incremental update to take place instead. The advantage is that you the programmer do not have to give any attention or write any code to make the incremental updates of the UI happen, and they are guaranteed correct. It might seem that this re-execution would be a performance problem, but it is not, since updating controls that do not need to be changed takes on the order of tens of nanoseconds.
1 class Patient {public:
2 String name;
3 double age;
4 bool smoker; // smoker only relevant if age >= 50
5 };
6 vector< Patient* > patients;
10 void deContents(){ int i;
11 // First, have a label
12 deLabel(200, 20, “Patient name, age, smoker:”);
13 // For each patient, have a row of controls
14 FOR(i=0, i<patients.Count(), i++)
15 deEditOnePatient( P( patients[i] ) );
16 END
17 // Have a button to add a patient
18 if (deButton(50, 20, “Add”)){
19 // When the button is clicked add the patient
20 patients.Add(new Patient);
21 DD_THROW;
22 }
23 }
30 void deEditOnePatient(Patient* p){
31 // Determine field widths
32 int w = (Width()-50)/3;
33 // Controls are laid out horizontally
34 deStartHorizontal();
35 // Have a button to remove this patient
36 if (deButton(50, 20, “Remove”)){
37 patients.Remove(p);
37 DD_THROW;
39 }
40 // Edit fields for name and age
41 deEdit(w, 20, P(&p->name));
42 deEdit(w, 20, P(&p->age));
43 // If age >= 50 have a checkbox for smoker boolean
44 IF(p->age >= 50)
45 deCheckBox(w, 20, “Smoker?”, P(&p->smoker));
46 END
47 deEndHorizontal(20);
48 }
Added: Brian asked a good question, and I thought the answer belonged in the main text here:
#Mike: I'm not clear on what the "if (deButton(50, 20, “Add”)){" statement is actually doing. What does the deButton function do? Also, are your FOR/END loops using some sort of macro or something? – Brian.
#Brian: Yes, the FOR/END and IF statements are macros. The SourceForge project has a complete implementation. deButton maintains a button control. When any user input action takes place, the code is run in "control event" mode, in which deButton detects that it was pressed and signifies that it was pressed by returning TRUE. Thus, the "if(deButton(...)){... action code ...} is a way of attaching action code to the button, without having to create a closure or write an event handler. The DD_THROW is a way of terminating the pass when the action is taken because the action may have modified application data, so it is invalid to continue the "control event" pass through the routine. If you compare this to writing event handlers, it saves you writing those, and it lets you have any number of controls.
Added: Sorry, I should explain what I mean by the word "maintains". When the procedure is first executed (in SHOW mode), deButton creates a button control and remembers its id in the FIFO. On subsequent passes (in UPDATE mode), deButton gets the id from the FIFO, modifies it if necessary, and puts it back in the FIFO. In ERASE mode, it reads it from the FIFO, destroys it, and does not put it back, thereby "garbage collecting" it. So the deButton call manages the entire lifetime of the control, keeping it in agreement with application data, which is why I say it "maintains" it.
The fourth mode is EVENT (or CONTROL). When the user types a character or clicks a button, that event is caught and recorded, and then the deContents procedure is executed in EVENT mode. deButton gets the id of its button control from the FIFO and askes if this is the control that was clicked. If it was, it returns TRUE so the action code can be executed. If not, it just returns FALSE. On the other hand, deEdit(..., &myStringVar) detects if the event was meant for it, and if so passes it to the edit control, and then copies the contents of the edit control to myStringVar. Between this and normal UPDATE processing, myStringVar always equals the contents of the edit control. That is how "binding" is done. The same idea applies to scroll bars, list boxes, combo boxes, any kind of control that lets you edit application data.
Here's a link to my Wikipedia edit: http://en.wikipedia.org/wiki/User:MikeDunlavey/Difex_Article
Differential execution is a strategy for changing the flow of your code based on external events. This is usually done by manipulating a data structure of some kind to chronicle the changes. This is mostly used in graphical user interfaces, but is also used for things like serialization, where you are merging changes into an existing "state."
The basic flow is as follows:
Start loop:
for each element in the datastructure:
if element has changed from oldDatastructure:
copy element from datastructure to oldDatastructure
execute corresponding subroutine (display the new button in your GUI, for example)
End loop:
Allow the states of the datastructure to change (such as having the user do some input in the GUI)
The advantages of this are a few. One, it is separation
of the execution of your changes, and the actual
manipulation of the supporting data. Which is nice for
multiple processors. Two, it provides a low bandwidth method
of communicating changes in your program.
Think of how a monitor works:
It is updated at 60 Hz -- 60 times a second. Flicker flicker flicker 60 times, but your eyes are slow and can't really tell. The monitor shows whatever is in the output buffer; it just drags this data out every 1/60th of a second no matter what you do.
Now why would you want your program to update the whole buffer 60 times a second if the image shouldn't change that often? What if you only change one pixel of the image, should you rewrite the entire buffer?
This is an abstraction of the basic idea: you want to change the output buffer based on what information you want displayed on the screen. You want to save as much CPU time and buffer write time as possible, so you don't edit parts of the buffer that need not be changed for the next screen pull.
The monitor is separate from your computer and logic (programs). It reads from the output buffer at whatever rate it updates the screen. We want our computer to stop synchronizing and redrawing unnecessarily. We can solve this by changing how we work with the buffer, which can be done in a variety of ways. His technique implements a FIFO queue that is on delay -- it holds what we just sent to the buffer. The delayed FIFO queue does not hold pixel data, it holds "shape primitives" (which might be pixels in your application, but it could also be lines, rectangles, easy-to-draw things because they are just shapes, no unnecessary data is allowed).
So you want to draw/erase things from the screen? No problem. Based on the contents of the FIFO queue I know what the monitor looks like at the moment. I compare my desired output (to erase or draw new primitives) with the FIFO queue and only change values that need to be changed/updated. This is the step which gives it the name Differential Evaluation.
Two distinct ways in which I appreciate this:
The First:
Mike Dunlavey uses a conditional-statement extension. The FIFO queue contains a lot of information (the "previous state" or the current stuff on monitor or time-based polling device). All you have to add to this is the state you want to appear on screen next.
A conditional bit is added to every slot that can hold a primitive in the FIFO queue.
0 means erase
1 means draw
However, we have previous state:
Was 0, now 0: don't do anything;
Was 0, now 1: add it to the buffer (draw it);
Was 1, now 1: don't do anything;
Was 1, now 0: erase it from the buffer (erase it from the screen);
This is elegant, because when you update something you really only need to know what primitives you want to draw to the screen -- this comparison will find out if it should erase a primitive or add/keep it to/in the buffer.
The Second:
This is just one example, and I think that what Mike is really getting at is something that should be fundamental in design for all projects: Reduce the (computational) complexity of design by writing your most computationally intense operations as computerbrain-food or as close as you can get. Respect the natural timing of devices.
A redraw method to draw the entire screen is incredibly costly, and there are other applications where this insight is incredibly valuable.
We are never "moving" objects around the screen. "Moving" is a costly operation if we are going to mimic the physical action of "moving" when we design code for something like a computer monitor. Instead, objects basically just flicker on and off with the monitor. Every time an object moves, it's now a new set of primitives and the old set of primitives flickers off.
Every time the monitor pulls from the buffer we have entries that look like
Draw bit primitive_description
0 Rect(0,0,5,5);
1 Circ(0,0,2);
1 Line(0,1,2,5);
Never does an object interact with the screen (or time-sensitive polling device). We can handle it more intelligently than an object will when it greedily asks to update the whole screen just to show a change specific to only itself.
Say we have a list of all possible graphical primitives our program is capable of generating, and that we tie each primitive to a set of conditional statements
if (iWantGreenCircle && iWantBigCircle && iWantOutlineOnMyCircle) ...
Of course, this is an abstraction and, really, the set of conditionals that represents a particular primitive being on/off could be large (perhaps hundreds of flags that must all evaluate to true).
If we run the program, we can draw to the screen at essentially the same rate at which we can evaluate all these conditionals. (Worst case: how long it takes to evaluate the largest set of conditional statements.)
Now, for any state in the program, we can simply evaluate all the conditionals and output to the screen lightning-quick! (We know our shape primitives and their dependent if-statements.)
This would be like buying a graphically-intense game. Only instead of installing it to your HDD and running it through your processor, you buy a brand-new board that holds the entirety of the game and takes as input: mouse, keyboard, and takes as output: monitor. Incredibly condensed conditional evaluation (as the most fundamental form of a conditional is logic gates on circuit boards). This would, naturally, be very responsive, but it offers almost no support in fixing bugs, as the whole board design changes when you make a tiny design change (because the "design" is so far-removed from the nature of the circuit board). At the expense of flexibility and clarity in how we represent data internally we have gained significant "responsiveness" because we are no longer doing "thinking" in the computer; it is all just reflex for the circuit board based on the inputs.
The lesson, as I understand it, is to divide labor such that you give each part of the system (not necessarily just computer and monitor) something it can do well. The "computer thinking" can be done in terms of concepts like objects... The computer brain will gladly try and think this all through for you, but you can simplify the task a great deal if you are able to let the computer think in terms of data_update and conditional_evals. Our human abstractions of concepts into code are idealistic, and in the case of internal program draw methods a little overly idealistic. When all you want is a result (array of pixels with correct color values) and you have a machine that can easily spit out an array that big every 1/60th of a second, try and eliminate as much flowery thinking from the computer brain as possible so that you can focus on what you really want: to synchronize your graphical updates with your (fast) inputs and the natural behavior of the monitor.
How does this map to other applications?
I'd like to hear of other examples, but I'm sure there are many. I think anything that provides a real-time "window" into the state of your information (variable state or something like a database... a monitor is just a window into your display buffer) can benefit from these insights.
I find this concept very similar to the state machines of classic digital electronics. Specially the ones which remember their previous output.
A machine whose next output depends on current input and previous output according to (YOUR CODE HERE). This current input is nothing but previous output + (USER, INTERACT HERE).
Fill up a surface with such machines, and it will be user interactive and at the same time represent a layer of changeable data. But at this stage it will still be dumb, just reflecting user interaction to underlying data.
Next, interconnect the machines on your surface, let them share notes, according to (YOUR CODE HERE), and now we make it intelligent. It will become an interactive computing system.
So you just have to provide your logic at two places in the above model; the rest is taken care of by the machine design itself. That's what good about it.

Resources