[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
RFC: Design for undo/redo support in drgeo/* code...
Hello everybody,
Here is a piece of design I have written to describe a possible
implementation of undo and redo support in the Dr. Geo code. Please
make comments on it.
Hilaire: when we come to an agreement on the way to implement this I
think we can split the work between us to speed up the development.
Implementing undo/redo support in Dr. Geo
=========================================
Some of the ideas explained here come from a very good book on object
oriented programming that I frequently read: "Design Patterns,
elements of reusable object-oriented software".
What we want to do is to implement undo/redo support for Dr. Geo
figures. We want undo/redo for all kinds of operations on a geometric
figure, and most notably:
* Creation of new item in the figure.
* Changing the attributes of one item (color, style, name).
* Moving an item on the figure.
* Removing an item from the figure.
* Running a macro.
We also want to support potentially unlimited number of undo/redo.
What I first recommend is that the following methods are implemented
in the drgeoFigure class:
* createItem ()
* setItemColor ()
* setItemDrawStyle ()
* setItemName ()
* runMacro ()
* removeItem ()
Implementing these methods will also help support scripting as all the
functionality of the Dr. Geo code is accessible through these methods.
When running one of these commands we must in some way remember the
state of the figure before the execution of the command, so that we
are able to undo the change. We also need to remember the parameters
of the method, so that we are able to redo the change.
As a complement to these public methods corresponding private methods
implementing just the functionality without taking care of undo/redo.
These private methods are used to perform the real work. I propose
that they are named as follow:
* _createItem ()
* _setItemColor ()
* _setItemDrawStyle ()
* _setItemName ()
* _runMacro ()
* _removeItem ()
Based on an idea exposed in the book I cited in the introduction I
suggest that we define the following class:
class drgeoCommand {
public:
virtual ~drgeoCommand ();
virtual void Execute ();
virtual void Unexecute ();
protected:
drgeoCommand ();
};
And now here is an example of how to use that class based on the
example of the command "removeItem()". First we have to sub-class the
drgeoCommand class:
class drgeoRemoveItemCommand {
public:
~drgeoRemoveItemCommand ();
drgeoRemoveItemCommand (drgeoFigure *aFigure, const char *aItem);
void Execute ();
void Unexecute ();
private:
// We keep all the information for undo/redo in the private part.
drgeoFigure *mFigure;
char *mItem;
XXX *mBackup;
};
Here is now a start of implementation of the methods of this class:
drgeoRemoveItemCommand::~drgeoRemoveItemCommand ()
{
if (mBackup)
delete mBackup;
}
drgeoRemoveItemCommand::drgeoRemoveItemCommand (drgeoFigure *aFigure,
const char *aItem)
{
mFigure = aFigure;
mItem = aItem;
mBackup = NULL;
}
void drgeoRemoveItemCommand::Execute ()
{
// Before removing the item, we must save data about it, so that
// it can be restored later. Is suggest to keep information about
// it as an XML data structure.
mBackup = mFigure->saveItem (mItem);
mFigure->_removeItem (mItem);
}
void drgeoRemoveItemCommand::Unexecute ()
{
// We have to re-create the item from the data we have previously
// saved. I suggest that we use an XML data structure.
mFigure->restoreItem (mBackup);
}
And finally here is how it is used in the implementation of
drgeoFigure::removeItem():
void drgeoFigure::removeItem (char *aItem)
{
// Create the command object.
drgeoCommand *command = new drgeoRemoveItemCommand (this, aItem);
// Run the command.
command->Execute ();
// We'll have to add code here to keep track of this command in an
// history. Read more about the history mechanism below.
}
When implementing the saveItem() and restoreItem() methods special
care must be taken of some details. In particular let's take the
example of the following sequence of event starting with an empty
figure:
* create point A.
* create point B.
* create segment [AB].
* create C the middle of segment [AB].
* undo creation of C.
* undo creation of [AB].
* redo [AB].
* redo C.
At this point when recreating C the delicate point is that the
reference to [AB] in the backup data of the creation command even
when [AB] has been undone/recreated (and possibly has a new id).
To implement undo/redo for macros, what can be done is to create a
drgeoMacroCommand class that will perform undo/redo on a list of
subcommands.
To support an unlimited number of undo we must keep an history of the
commands executed on the figure. The history data-structure is a kind
of LIFO queue (like a stack with a push/pop like mechanism).
When doing several undos we must keep the full history so that we are
able to redo operation, but if instead of redoing an operation we
perform a new operation on the figure, then we must discard all the
"end" of the command history.
Additional idea:
================
Maybe we could save the undo/redo history as XML so that when loading
a figure with an history we can play a scenario, just by pressing the
"redo" button several times.
I'm waiting for your comments, Laurent.
--
Laurent Gauthier is lolo@seul.org +;+ ``Keep it simple: as simple as possible,
at home ;:; but no simpler.''
in Nozay (France) +;+ -- A. Einstein