Dia's Undo System
Dia's Undo system uses a stack of Change objects that each describe how to undo/redo an action and provide a way to free the object. These objects typically contain some state information about the action, such as a pointer to a deleted object, a text entered or a set of properties changed. Note that there may be required information both for undoing and for redoing.
The core of the undo system is defined in app/undo.c and app/undo.h. In particular, app/undo.h defines the following types:
typedef void (*UndoApplyFunc)(Change *change, Diagram *dia);
typedef void (*UndoRevertFunc)(Change *change, Diagram *dia);
typedef void (*UndoFreeFunc)(Change *change);
struct _Change {
/* If apply == transaction_point_pointer then this is a transaction
point */
UndoApplyFunc apply;
UndoRevertFunc revert;
UndoFreeFunc free; /* Remove extra data. Then this object is freed */
Change *prev, *next;
};dia-types.h defines Change to be the same as struct _Change.
The prev and next pointers should never be used outside of app/undo.c.
Defining Change objects
Example
This is the undo definition for reordering objects (i.e. moving them up/down in the layer):
/******** Reorder object list: */
struct ReorderObjectsChange {
Change change;
Layer *layer;
GList *changed_list; /* Owning reference when applied */
GList *original_objects;
GList *reordered_objects;
};
static void
reorder_objects_apply(struct ReorderObjectsChange *change, Diagram *dia)
{
DEBUG_PRINTF(("reorder_objects_apply()\n"));
layer_set_object_list(change->layer,
g_list_copy(change->reordered_objects));
object_add_updates_list(change->changed_list, dia);
}
static void
reorder_objects_revert(struct ReorderObjectsChange *change, Diagram *dia)
{
DEBUG_PRINTF(("reorder_objects_revert()\n"));
layer_set_object_list(change->layer,
g_list_copy(change->original_objects));
object_add_updates_list(change->changed_list, dia);
}
static void
reorder_objects_free(struct ReorderObjectsChange *change)
{
DEBUG_PRINTF(("reorder_objects_free()\n"));
g_list_free(change->changed_list);
g_list_free(change->original_objects);
g_list_free(change->reordered_objects);
}
Change *
undo_reorder_objects(Diagram *dia, GList *changed_list, GList *orig_list)
{
struct ReorderObjectsChange *change;
change = g_new0(struct ReorderObjectsChange, 1);
change->change.apply = (UndoApplyFunc) reorder_objects_apply;
change->change.revert = (UndoRevertFunc) reorder_objects_revert;
change->change.free = (UndoFreeFunc) reorder_objects_free;
change->layer = dia->data->active_layer;
change->changed_list = changed_list;
change->original_objects = orig_list;
change->reordered_objects = g_list_copy(dia->data->active_layer->objects);
DEBUG_PRINTF(("UNDO: Push new reorder objects at %d\n", depth(dia->undo)));
undo_push_change(dia->undo, (Change *) change);
return (Change *)change;
}