Re: Undo and the text widget

Havoc Pennington wrote:


I'm hoping people can give me some input on the text widget and
implementing an "undo" facility.

What I've done at the moment is that the "insert_text" and
"delete_text" signals have an "interactive" flag, that indicates
whether the insertion or deletion is the result of user action.

The problem with this is that one user action can result in multiple
signals. e.g. say you select a range of text that includes uneditable
text and press delete, only the possibly discontiguous editable chunks
will be deleted. Or the way meta-space works is that it deletes all
spaces, then inserts one space.

An alternative to the flag indicating whether an insertion or deletion
is interactive might be:
void gtk_text_buffer_begin_user_action (GtkTextBuffer *buffer);
 void gtk_text_buffer_end_user_action        (GtkTextBuffer *buffer);
 int  gtk_text_buffer_get_user_action_serial (GtkTextBuffer *buffer);

The idea is that each user action is identified by a serial number. To implement meta-space for example, you'd do:

 begin_user_action ();
 delete_spaces_around_cursor ();
 insert_a_space ();
 end_user_action ();

During the delete/insert, the user action serial number would be the
same. If no user action is pending, the current user action serial
number would be 0.

begin_user_action() checks whether there is currently a user action
pending. If so, it just increments a counter for "depth of user action
nesting" and does nothing else. If not, it sets a new user action
serial number.
end_user_action() decrements the "depth of user actions" counter, and
if the counter reaches 0, resets the serial number to 0.

This allows you to compose a single user action out of smaller user
actions. For example, a search-and-replace could make all the replaces
be the same action. The outermost begin/end pair defines the active serial number.

So, to implement undo, in your insert_text or delete_text signal
handler, you'd see whether the current user action serial is
nonzero. If the current serial matches the serial of the topmost item on the undo stack, you add the insert or delete to the topmost
undo item. If it's different, you create a new undo item and push it
on the undo stack.

Anyone see any problems with this approach?

You might want to consider breaking the undo and the text widget into seperate components instead of making the transaction management part of the editor's api.

Is it possible to break the text widget's actions down into chunks that you can play forward and back? That way you would get redo and undo. You could pass in something like:

"Add" "Type" <char code> "Offset"
"Delete" "Cut" <char code> "Offset"

You could also use that to build macroified text actions - it lends itself to scriptability. You could build the transaction manager around it instead of as a part of the editor. You could also hook up to the key signals in the editor widget and build your own transactions from that instead of having a hard coded transaction behaviour in the editor widget.

Sorry if this sounds a little disjointed, I'm in a meeting. :)


Christopher Blizzard

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]