2005-11-14 21:56:39 -08:00
|
|
|
/*
|
2006-05-21 09:18:11 -07:00
|
|
|
* mooundo.c
|
2005-11-14 21:56:39 -08:00
|
|
|
*
|
2007-06-24 10:56:20 -07:00
|
|
|
* Copyright (C) 2004-2007 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
2005-11-14 21:56:39 -08:00
|
|
|
*
|
2007-06-24 10:56:20 -07:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
2007-09-23 09:47:28 -07:00
|
|
|
* License version 2.1 as published by the Free Software Foundation.
|
2005-11-14 21:56:39 -08:00
|
|
|
*
|
|
|
|
* See COPYING file that comes with this distribution.
|
|
|
|
*/
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
#include "mooutils/mooundo.h"
|
2005-11-14 21:56:39 -08:00
|
|
|
#include "mooutils/moomarshals.h"
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
A bit of how and why:
|
|
|
|
1) Instead of keeping one list of actions and pointer to 'current' one,
|
|
|
|
it keeps two stacks - undo and redo. On Undo, action from undo stack is
|
|
|
|
'undoed' and pushed into redo stack, and vice versa. When new action
|
|
|
|
is added, redo stack is erased.
|
|
|
|
These stacks are lists now, but they should be queues if we want to limit
|
|
|
|
number of actions in the stack.
|
|
|
|
2) Those stacks contain ActionGroup's, each ActionGroup is a queue of
|
|
|
|
UndoAction instances.
|
|
|
|
3) How actions are added:
|
|
|
|
UndoMgr may be 'frozen' - then it discards added actions, it's
|
|
|
|
gtk_source_undo_manager_(begin|end)_not_undoable_action().
|
|
|
|
One may tell UndoMgr not to split action groups - analog of what
|
|
|
|
GtkSourceUndoManager did for user_action signal. It's done by incrementing
|
|
|
|
continue_group count.
|
|
|
|
One may force UndoMgr to create new group - by new_group flag.
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
Now, in the undo_stack_add_action() call it does the following:
|
2005-11-14 21:56:39 -08:00
|
|
|
if it's frozen, free the action, and do nothing;
|
|
|
|
if new_group is requested, create new undo group and push it on the stack;
|
|
|
|
otherwise, try to merge given action into existing group. If it can be
|
|
|
|
merged, fine. If it can't be merged, then create new group.
|
|
|
|
Here's a problem with continue_group thing: when we ask for it, we want
|
|
|
|
to merge following actions with this one. But what do we want to do with this one?
|
|
|
|
ATM I made a do_continue flag which means "don't create new group" and is set
|
|
|
|
_after_ adding first action. It works fine for text buffer, and for entry, but
|
|
|
|
it's ugly and not clear.
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
GQueue *actions;
|
|
|
|
} ActionGroup;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
guint type;
|
|
|
|
MooUndoAction *action;
|
|
|
|
} Wrapper;
|
|
|
|
|
|
|
|
|
|
|
|
static MooUndoActionClass *types;
|
|
|
|
static guint last_type;
|
|
|
|
|
|
|
|
#define WRAPPER_VTABLE(wrapper__) (&types[(wrapper__)->type])
|
|
|
|
#define TYPE_VTABLE(type__) (&types[type__])
|
|
|
|
#define TYPE_IS_REGISTERED(type__) ((type__) && (type__) <= last_type)
|
|
|
|
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
static void moo_undo_stack_finalize (GObject *object);
|
|
|
|
static void moo_undo_stack_set_property (GObject *object,
|
2005-11-14 21:56:39 -08:00
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec);
|
2006-05-21 09:18:11 -07:00
|
|
|
static void moo_undo_stack_get_property (GObject *object,
|
2005-11-14 21:56:39 -08:00
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec);
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
static void moo_undo_stack_undo_real (MooUndoStack *stack);
|
|
|
|
static void moo_undo_stack_redo_real (MooUndoStack *stack);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
|
|
|
static void action_stack_free (GSList **stack,
|
|
|
|
gpointer doc);
|
|
|
|
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
G_DEFINE_TYPE(MooUndoStack, moo_undo_stack, G_TYPE_OBJECT)
|
2005-11-14 21:56:39 -08:00
|
|
|
|
|
|
|
enum {
|
|
|
|
PROP_0,
|
|
|
|
PROP_DOCUMENT,
|
|
|
|
PROP_CAN_UNDO,
|
|
|
|
PROP_CAN_REDO
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
UNDO,
|
|
|
|
REDO,
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint signals[LAST_SIGNAL];
|
|
|
|
|
|
|
|
static void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_class_init (MooUndoStackClass *klass)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
gobject_class->set_property = moo_undo_stack_set_property;
|
|
|
|
gobject_class->get_property = moo_undo_stack_get_property;
|
|
|
|
gobject_class->finalize = moo_undo_stack_finalize;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
klass->undo = moo_undo_stack_undo_real;
|
|
|
|
klass->redo = moo_undo_stack_redo_real;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_DOCUMENT,
|
|
|
|
g_param_spec_pointer ("document",
|
|
|
|
"document",
|
|
|
|
"document",
|
|
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_CAN_UNDO,
|
|
|
|
g_param_spec_boolean ("can-undo",
|
|
|
|
"can-undo",
|
|
|
|
"can-undo",
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READABLE));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_CAN_REDO,
|
|
|
|
g_param_spec_boolean ("can-redo",
|
|
|
|
"can-redo",
|
|
|
|
"can-redo",
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READABLE));
|
|
|
|
|
|
|
|
signals[UNDO] =
|
|
|
|
g_signal_new ("undo",
|
|
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
2006-05-21 09:18:11 -07:00
|
|
|
G_STRUCT_OFFSET (MooUndoStackClass, undo),
|
2005-11-14 21:56:39 -08:00
|
|
|
NULL, NULL,
|
|
|
|
_moo_marshal_VOID__VOID,
|
|
|
|
G_TYPE_NONE, 0);
|
|
|
|
|
|
|
|
signals[REDO] =
|
|
|
|
g_signal_new ("redo",
|
|
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
2006-05-21 09:18:11 -07:00
|
|
|
G_STRUCT_OFFSET (MooUndoStackClass, redo),
|
2005-11-14 21:56:39 -08:00
|
|
|
NULL, NULL,
|
|
|
|
_moo_marshal_VOID__VOID,
|
|
|
|
G_TYPE_NONE, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_init (G_GNUC_UNUSED MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
MooUndoStack*
|
|
|
|
moo_undo_stack_new (gpointer document)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
return g_object_new (MOO_TYPE_UNDO_STACK,
|
2005-11-14 21:56:39 -08:00
|
|
|
"document", document,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_finalize (GObject *object)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
MooUndoStack *stack = MOO_UNDO_STACK (object);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
action_stack_free (&stack->undo_stack, stack->document);
|
|
|
|
action_stack_free (&stack->redo_stack, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
G_OBJECT_CLASS(moo_undo_stack_parent_class)->finalize (object);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_set_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
MooUndoStack *stack = MOO_UNDO_STACK (object);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_DOCUMENT:
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->document = g_value_get_pointer (value);
|
2005-11-14 21:56:39 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_get_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
MooUndoStack *stack = MOO_UNDO_STACK (object);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_DOCUMENT:
|
2006-05-21 09:18:11 -07:00
|
|
|
g_value_set_pointer (value, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_CAN_UNDO:
|
2006-05-21 09:18:11 -07:00
|
|
|
g_value_set_boolean (value, moo_undo_stack_can_undo (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_CAN_REDO:
|
2006-05-21 09:18:11 -07:00
|
|
|
g_value_set_boolean (value, moo_undo_stack_can_redo (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
guint
|
|
|
|
moo_undo_action_register (MooUndoActionClass *klass)
|
|
|
|
{
|
|
|
|
MooUndoActionClass *tmp;
|
|
|
|
|
|
|
|
g_return_val_if_fail (klass != NULL, 0);
|
|
|
|
g_return_val_if_fail (klass->undo != NULL && klass->redo != NULL, 0);
|
|
|
|
g_return_val_if_fail (klass->merge != NULL && klass->destroy != NULL, 0);
|
|
|
|
|
|
|
|
tmp = g_new (MooUndoActionClass, last_type + 2);
|
|
|
|
if (last_type)
|
|
|
|
memcpy (tmp, types, sizeof(MooUndoActionClass) * (last_type + 1));
|
|
|
|
g_free (types);
|
|
|
|
types = tmp;
|
|
|
|
|
|
|
|
memcpy (&types[last_type+1], klass, sizeof(MooUndoActionClass));
|
|
|
|
|
|
|
|
return ++last_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_undo (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
|
|
|
g_signal_emit (stack, signals[UNDO], 0);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_redo (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
|
|
|
g_signal_emit (stack, signals[REDO], 0);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gboolean
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_can_undo (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_val_if_fail (MOO_IS_UNDO_STACK (stack), FALSE);
|
|
|
|
return stack->undo_stack != NULL;
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gboolean
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_can_redo (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_val_if_fail (MOO_IS_UNDO_STACK (stack), FALSE);
|
|
|
|
return stack->redo_stack != NULL;
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
action_group_undo (ActionGroup *group,
|
2006-05-21 09:18:11 -07:00
|
|
|
MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
GList *l;
|
|
|
|
|
|
|
|
for (l = group->actions->head; l != NULL; l = l->next)
|
|
|
|
{
|
|
|
|
Wrapper *wrapper = l->data;
|
2006-05-21 09:18:11 -07:00
|
|
|
WRAPPER_VTABLE(wrapper)->undo (wrapper->action, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
action_group_redo (ActionGroup *group,
|
2006-05-21 09:18:11 -07:00
|
|
|
MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
GList *l;
|
|
|
|
|
|
|
|
for (l = group->actions->tail; l != NULL; l = l->prev)
|
|
|
|
{
|
|
|
|
Wrapper *wrapper = l->data;
|
2006-05-21 09:18:11 -07:00
|
|
|
WRAPPER_VTABLE(wrapper)->redo (wrapper->action, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_undo_real (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
ActionGroup *group;
|
|
|
|
GSList *link;
|
|
|
|
gboolean notify_redo;
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (stack->undo_stack != NULL);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->frozen++;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
link = stack->undo_stack;
|
2005-11-14 21:56:39 -08:00
|
|
|
group = link->data;
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->undo_stack = g_slist_delete_link (stack->undo_stack, link);
|
|
|
|
notify_redo = stack->redo_stack == NULL;
|
|
|
|
stack->redo_stack = g_slist_prepend (stack->redo_stack, group);
|
|
|
|
stack->new_group = TRUE;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
action_group_undo (group, stack);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->frozen--;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_freeze_notify (G_OBJECT (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
if (!stack->undo_stack)
|
|
|
|
g_object_notify (G_OBJECT (stack), "can-undo");
|
2005-11-14 21:56:39 -08:00
|
|
|
if (notify_redo)
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_notify (G_OBJECT (stack), "can-redo");
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_thaw_notify (G_OBJECT (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_redo_real (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
ActionGroup *group;
|
|
|
|
GSList *link;
|
|
|
|
gboolean notify_undo;
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (stack->redo_stack != NULL);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->frozen++;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
link = stack->redo_stack;
|
2005-11-14 21:56:39 -08:00
|
|
|
group = link->data;
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->redo_stack = g_slist_delete_link (stack->redo_stack, link);
|
|
|
|
notify_undo = stack->undo_stack == NULL;
|
|
|
|
stack->undo_stack = g_slist_prepend (stack->undo_stack, group);
|
|
|
|
stack->new_group = TRUE;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
action_group_redo (group, stack);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->frozen--;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_freeze_notify (G_OBJECT (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
if (!stack->redo_stack)
|
|
|
|
g_object_notify (G_OBJECT (stack), "can-redo");
|
2005-11-14 21:56:39 -08:00
|
|
|
if (notify_undo)
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_notify (G_OBJECT (stack), "can-undo");
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_thaw_notify (G_OBJECT (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Wrapper *
|
|
|
|
wrapper_new (guint type,
|
|
|
|
MooUndoAction *action)
|
|
|
|
{
|
|
|
|
Wrapper *w = g_new0 (Wrapper, 1);
|
|
|
|
w->type = type;
|
|
|
|
w->action = action;
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
wrapper_free (Wrapper *wrapper,
|
|
|
|
gpointer doc)
|
|
|
|
{
|
|
|
|
if (wrapper)
|
|
|
|
{
|
|
|
|
WRAPPER_VTABLE(wrapper)->destroy (wrapper->action, doc);
|
|
|
|
g_free (wrapper);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
action_group_free (ActionGroup *group,
|
|
|
|
gpointer doc)
|
|
|
|
{
|
|
|
|
if (group)
|
|
|
|
{
|
|
|
|
g_queue_foreach (group->actions, (GFunc) wrapper_free, doc);
|
|
|
|
g_queue_free (group->actions);
|
|
|
|
g_free (group);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ActionGroup*
|
|
|
|
action_group_new (void)
|
|
|
|
{
|
|
|
|
ActionGroup *group = g_new0 (ActionGroup, 1);
|
|
|
|
group->actions = g_queue_new ();
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
action_stack_free (GSList **stack,
|
|
|
|
gpointer doc)
|
|
|
|
{
|
|
|
|
g_slist_foreach (*stack, (GFunc) action_group_free, doc);
|
|
|
|
g_slist_free (*stack);
|
|
|
|
*stack = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_clear (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
gboolean notify_redo, notify_undo;
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
notify_undo = stack->undo_stack != NULL;
|
|
|
|
notify_redo = stack->redo_stack != NULL;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
action_stack_free (&stack->undo_stack, stack->document);
|
|
|
|
action_stack_free (&stack->redo_stack, stack->document);
|
|
|
|
stack->new_group = FALSE;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_freeze_notify (G_OBJECT (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
|
|
|
|
if (notify_undo)
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_notify (G_OBJECT (stack), "can-undo");
|
2005-11-14 21:56:39 -08:00
|
|
|
if (notify_redo)
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_notify (G_OBJECT (stack), "can-redo");
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_thaw_notify (G_OBJECT (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
action_group_merge (ActionGroup *group,
|
|
|
|
guint type,
|
|
|
|
MooUndoAction *action,
|
|
|
|
gpointer doc)
|
|
|
|
{
|
|
|
|
Wrapper *old;
|
|
|
|
|
|
|
|
old = group->actions->head ? group->actions->head->data : NULL;
|
|
|
|
|
|
|
|
if (!old || old->type != type)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (WRAPPER_VTABLE(old)->merge (old->action, action, doc))
|
|
|
|
{
|
|
|
|
TYPE_VTABLE(type)->destroy (action, doc);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
action_group_add (ActionGroup *group,
|
|
|
|
guint type,
|
|
|
|
MooUndoAction *action,
|
|
|
|
gboolean try_merge,
|
|
|
|
gpointer doc)
|
|
|
|
{
|
|
|
|
if (!try_merge || !action_group_merge (group, type, action, doc))
|
|
|
|
{
|
|
|
|
Wrapper *wrapper = wrapper_new (type, action);
|
|
|
|
g_queue_push_head (group->actions, wrapper);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_add_action (MooUndoStack *stack,
|
|
|
|
guint type,
|
|
|
|
MooUndoAction *action)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
gboolean notify_redo, notify_undo;
|
|
|
|
ActionGroup *group;
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
g_return_if_fail (action != NULL);
|
|
|
|
g_return_if_fail (TYPE_IS_REGISTERED (type));
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
if (stack->frozen)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
TYPE_VTABLE(type)->destroy (action, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
notify_undo = stack->undo_stack == NULL;
|
|
|
|
notify_redo = stack->redo_stack != NULL;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
if (!stack->undo_stack || stack->new_group)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
group = action_group_new ();
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->undo_stack = g_slist_prepend (stack->undo_stack, group);
|
|
|
|
action_group_add (group, type, action, FALSE, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
2006-05-21 09:18:11 -07:00
|
|
|
else if (stack->do_continue)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
group = stack->undo_stack->data;
|
|
|
|
action_group_add (group, type, action, TRUE, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
group = stack->undo_stack->data;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
if (!action_group_merge (group, type, action, stack->document))
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
|
|
|
group = action_group_new ();
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->undo_stack = g_slist_prepend (stack->undo_stack, group);
|
|
|
|
action_group_add (group, type, action, TRUE, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
stack->new_group = FALSE;
|
|
|
|
if (stack->continue_group)
|
|
|
|
stack->do_continue = TRUE;
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
action_stack_free (&stack->redo_stack, stack->document);
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_freeze_notify (G_OBJECT (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
|
|
|
|
if (notify_undo)
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_notify (G_OBJECT (stack), "can-undo");
|
2005-11-14 21:56:39 -08:00
|
|
|
if (notify_redo)
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_notify (G_OBJECT (stack), "can-redo");
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
g_object_thaw_notify (G_OBJECT (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_start_group (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
|
|
|
stack->continue_group++;
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_end_group (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
|
|
|
g_return_if_fail (stack->continue_group > 0);
|
|
|
|
if (!--stack->continue_group)
|
|
|
|
stack->do_continue = FALSE;
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_new_group (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
|
|
|
stack->new_group = TRUE;
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_freeze (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
2005-11-14 21:56:39 -08:00
|
|
|
|
2006-05-21 09:18:11 -07:00
|
|
|
if (!stack->frozen++)
|
|
|
|
moo_undo_stack_clear (stack);
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_thaw (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
g_return_if_fail (MOO_IS_UNDO_STACK (stack));
|
|
|
|
g_return_if_fail (stack->frozen > 0);
|
|
|
|
stack->frozen--;
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gboolean
|
2006-05-21 09:18:11 -07:00
|
|
|
moo_undo_stack_frozen (MooUndoStack *stack)
|
2005-11-14 21:56:39 -08:00
|
|
|
{
|
2006-05-21 09:18:11 -07:00
|
|
|
return stack->frozen > 0;
|
2005-11-14 21:56:39 -08:00
|
|
|
}
|