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;
}

Using Change objects

Apps/Dia/Undo (last edited 2013-08-09 00:08:16 by WilliamJonMcCann)