Re: Undo stack for GTK+ (was: Re: undo in textview)



Hi Paul,

thanks for your remarks!

On Di, 29.12.2009 21:48, Paul Davis wrote:

>It was designed after looking at undo models in various other
>applications, and finding them all to have various problems for what
>we really needed (this included the GIMP). 

This seems to be a common scenario. Let's work on fixing this in the
toolkit! :-)

>1) you need to be able to group individual actions into transactions

Absolutely.

>2) serialization/deserialization is good, and hard

That's a very interesting point that I've been thinking about too.
Though this seems like another topic to me - many widgets in GTK+ could
benefit from serialization support (e.g. state serialization of
TreeView's), so this should really be a more general interface and not
an undo-stack-isle-solution, so I left it out for now.

>3) its very hard to use only a Memento pattern without running into
>efficiency problems

Efficiency is a good point, too. For an undo stack, the most common
operation is to put stuff onto the stack, so this should be most
efficient. Pushing something onto the stack should be cheap, and
keeping a large stack around should be cheap (memory-wise).

That's why I chose elements of the stack just be be minimal structs,
just holding a (optional) human readable description, a pointer to
a struct of the callback functions, and a pointer to the user-data.
That's about as efficient as it can get.

An alternative implementation would be to have a base class, and each
element that can be pushed onto the stack needs to be derived from that
base class, overriding the callback functions as needed (that's btw how
Qt does it with the QUndoCommand class, and I believe also how you
did it). I also tried that in a previous attempt, but found it too
expensive (and hard to use from C) for little gain.

>5) deciding whether or not to support nested transactions can be important

True. Actually, my first attempt didn't allow nesting of transactions
(or groups, how I called it), and I found it insufficient. And this
limitation is not really needed anyways, if one doesn't distinguish
between individual command entries and transactions, but instead treats
a transaction as a special case of a command entry.

In my suggested implementation, the stack is a list of trees. In your
nomenclature, all leaves in the tree would be individual command
entries, and all non-leaves would be transactions. An undo operation
undoes the complete tree.

I find this nesting possibility essential for programs that don't e.g.
follow the command pattern from the start anyways.

>battle-tested implementation that avoids pre-registration
>of actions that can be undone

Actually, it would be very easy to avoid the registering of undo action
sets with a name, and just pass a pointer to the set whenever
registering an action. It could probably even be offered in parallel as
an alternative.

However, there's one important reason why the registering should at
least be possible: To make it possible to hook into the stack from
dynamically loaded modules (aka plugins). If they just registered
function pointers, and got unloaded, the pointers would point to
garbage, and undo operations would crash.

By allowing them to register, a "unregister" operation could be
provided which removed everything concerning that set from the stack
(or, it it wants to be conservative, cleared the stack if an entry
corresponding to the unregistered item was in it).

Holger


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