You're there! Goodie, I was afraid you were away on holidays or something. On Mon, 7 Jun 1999, [EMAIL PROTECTED] wrote: > On 6 Jun 1999, Lars R.Clausen wrote: >> Alexander, what thoughts have you made about the undo functionality? I >> could see how the properties settings would be one of the more difficult >> parts of that. > > Yes, this is the hardest part of undo. > > I've pushed the undo stuff into the future for too long, maybe it's time > to do now, before too much objects are written. Many objects will > probably need to be changed for this. > > Let's discuss the implementation (with multi-object-properties setting on > our mind too). It was a long time since i thought about this last time, > but i'll try to remeber what i thought then. > > First, undo can be done in two ways. Either you keep a copy of the whole > document (a snapshot) which can be reverted to easily. Or you store each > change incrementally such that you can revert it easily. > > The first way is bad, because it's ugly, memory-wastefull, slow and a > pain in the butt for pointer based structures like Dia. Agreed! > Therefore we need to do transaction-based undo. Each change to the > document (diagram) must be packed in a structure toghether with > information on how to revert it. These change-revert packets are then > placed on a stack (of finite or infinite length) and you have a pointer > to the last change (which is not always at the top, because you want redo > functionallity too). Because changes in the document originating in user > action often result in several 'smaller' changes in the diagram, which > must be undo:ed togheter, we also need to be able to group changes > togheter, or have transaction-points in the queue (where new undo/redo > starts). > > Now, most of the changes to the document in Dia must be wrapped up in > this way. Lets see how: > > * Scrolling, zooming, view changes: > Not part of undo. > > * Moving objects: The trivial part, one object moved to a new spot is > easy. Connections between objects complicates this a lot though. Moving > one object can in principle modify all objects in the diagram. The > variables changing here can be position, form, size, etc. These things > must be possible to extract from each object in an opaque data structure, > which can then be used to restore previous state. If a connection is > broken the corresponding connection object is not used. This must be > saved at the same address so that it can be reused later with old > pointers. I don't think this is that much of a problem. If we look at the situation right after a move is done, simply moving (with the standard move procedure) back to the original spot should automatically bring all connected objects back to their original state. Same goes for resize etc. If we have done other actions and then undo'ed back to this situation, we should still have the exact same connections, and thus a simple move will work again. If it was possible to break connections after an action, and then undo the action without undoing the breaking, then we have a much more complicated problem. The same goes for resizing, scaling, rotating etc. > > * Moving a handle: > This is much like moving objects. Same solution can probably be used. > > * Creating object: Pretty easy. Creating an object can connect it to > another though. Can this change the other object??? If this is undo:ed > and then redoed it must use the same memory position as before (no copy > tricks for undo stack) so that other redo's pointers are correct. That's a more interesting problem. > * Deleting objects: > Pretty easy. Might break connections. Can this change other objects? > > * Cut object: > Modifies the diagram like delete. Also modifies the clipboard. That must > be part of the undo-queue to. You're sure you want the clipboard modification to be part of the undo? Emacs, for instance, doesn't do that, and I regularly use undo-copy-redo to get something into the clipboard. > * Changing properties of an object: > This is a hard one. It must be possible to get and apply the 'properties' > data from an object. This is much like what's needed for move > object/handle, but this contains much more data and is used more seldom, > so should probably be separate call for memory reasons. > Maybe complete change of properties needed for multiple-object property > changes. Probably. The way properties are handled at the moment is not really nice, with each object re-implementing the dialog entries. > * Create layer: > Easy > > * Delete layer: > Needs to save possibly lots of data. Should this be undo:able? > Otherwise pretty easy, just store the whole layer in the undo stack. Seeing as how you can't have connections between layers (is that a bug or a feature? I think it's a feature:), I'd say keep the layer in the undo stack. > * Any more changes? > > > All changes needs to be packed together in a format which is easy to > handle in generic routines. This is easiest done with an object-oriented > solution. > > struct Change { > int first_change_in_transaction; > void (*apply)(struct Change *change, Diagram *dia); > void (*revert)(struct Change *change, Diagram *dia); > struct Change *prev, *next; > }; > > struct MoveChange { > struct Change change; > ... data for undo/redo ... > }; Looks fair to me. > struct Change *undo_stack; /* Points to top of stack. */ > struct Change *current_undo; /* Points to the last Change applied */ > > The first variable is used to find the first (and last) change that is > part of a transaction (a user-initiated change). Why not just have a last-transaction variable, and then have each transaction-delimiter store a link to the previous and next? BTW, this would be a per-diagram undo, right? -Lars -- Lars R. Clausen (http://shasta.cs.uiuc.edu/~lrclause) Hårdgrim of Westfield "I do not agree with a word that you say, but I will defend to the death your right to say it." -- Voltaire (?)