[gtksourceview] Refactor undo manager in public interface
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview] Refactor undo manager in public interface
- Date: Sat, 20 Feb 2010 16:43:06 +0000 (UTC)
commit 945390f92251da27455f8c79bd7263875cbe333a
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Sat Feb 20 15:26:07 2010 +0100
Refactor undo manager in public interface
This adds the GtkSourceUndoManager public interface which can be
implemented to override the default undo manager on a buffer. The
default undo manager is renamed to GtkSourceUndoManagerDefault
and will be used unless a different undo manager is set using
gtk_source_buffer_set_undo_manager.
gtksourceview/Makefile.am | 4 +-
gtksourceview/gtksourcebuffer.c | 181 ++++-
gtksourceview/gtksourcebuffer.h | 5 +
gtksourceview/gtksourceundomanager.c | 1199 ++++-----------------------
gtksourceview/gtksourceundomanager.h | 65 +-
gtksourceview/gtksourceundomanagerdefault.c | 1194 ++++++++++++++++++++++++++
gtksourceview/gtksourceundomanagerdefault.h | 64 ++
7 files changed, 1625 insertions(+), 1087 deletions(-)
---
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index 20d5f8c..563a8cf 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -34,6 +34,7 @@ libgtksourceview_headers = \
gtksourcemark.h \
gtksourceprintcompositor.h \
gtksourcegutter.h \
+ gtksourceundomanager.h \
gtksourcecompletion.h \
gtksourcecompletioninfo.h \
gtksourcecompletionitem.h \
@@ -51,7 +52,7 @@ NOINST_H_FILES = \
gtksourcegutter-private.h \
gtksourcelanguage-private.h \
gtksourcestyle-private.h \
- gtksourceundomanager.h \
+ gtksourceundomanagerdefault.h \
gtksourceview-i18n.h \
gtksourceview-utils.h \
gtktextregion.h
@@ -61,6 +62,7 @@ libgtksourceview_2_0_la_SOURCES = \
gtksourceiter.c \
gtksourceview.c \
gtksourceundomanager.c \
+ gtksourceundomanagerdefault.c \
gtktextregion.c \
gtksourcelanguage.c \
gtksourcelanguagemanager.c \
diff --git a/gtksourceview/gtksourcebuffer.c b/gtksourceview/gtksourcebuffer.c
index 82c03a8..edbcab8 100644
--- a/gtksourceview/gtksourcebuffer.c
+++ b/gtksourceview/gtksourcebuffer.c
@@ -39,6 +39,7 @@
#include "gtksourceiter.h"
#include "gtksourcestyleschememanager.h"
#include "gtksourcestyle-private.h"
+#include "gtksourceundomanagerdefault.h"
/**
* SECTION:buffer
@@ -100,7 +101,8 @@ enum {
PROP_HIGHLIGHT_MATCHING_BRACKETS,
PROP_MAX_UNDO_LEVELS,
PROP_LANGUAGE,
- PROP_STYLE_SCHEME
+ PROP_STYLE_SCHEME,
+ PROP_UNDO_MANAGER
};
struct _GtkSourceBufferPrivate
@@ -122,6 +124,7 @@ struct _GtkSourceBufferPrivate
GtkSourceStyleScheme *style_scheme;
GtkSourceUndoManager *undo_manager;
+ gint max_undo_levels;
};
G_DEFINE_TYPE (GtkSourceBuffer, gtk_source_buffer, GTK_TYPE_TEXT_BUFFER)
@@ -141,11 +144,9 @@ static void gtk_source_buffer_get_property (GObject
guint prop_id,
GValue *value,
GParamSpec *pspec);
-static void gtk_source_buffer_can_undo_handler (GtkSourceUndoManager *um,
- gboolean can_undo,
+static void gtk_source_buffer_can_undo_handler (GtkSourceUndoManager *manager,
GtkSourceBuffer *buffer);
-static void gtk_source_buffer_can_redo_handler (GtkSourceUndoManager *um,
- gboolean can_redo,
+static void gtk_source_buffer_can_redo_handler (GtkSourceUndoManager *manager,
GtkSourceBuffer *buffer);
static void gtk_source_buffer_real_insert_text (GtkTextBuffer *buffer,
GtkTextIter *iter,
@@ -229,7 +230,8 @@ gtk_source_buffer_class_init (GtkSourceBufferClass *klass)
/**
* GtkSourceBuffer:max-undo-levels:
*
- * Number of undo levels for the buffer. -1 means no limit.
+ * Number of undo levels for the buffer. -1 means no limit. This property
+ * will only affect the default undo manager.
*/
g_object_class_install_property (object_class,
PROP_MAX_UNDO_LEVELS,
@@ -284,6 +286,14 @@ gtk_source_buffer_class_init (GtkSourceBufferClass *klass)
GTK_TYPE_SOURCE_STYLE_SCHEME,
G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_UNDO_MANAGER,
+ g_param_spec_object ("undo-manager",
+ _("Undo manager"),
+ _("The buffer undo manager"),
+ GTK_TYPE_SOURCE_UNDO_MANAGER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
param_types[0] = GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE;
param_types[1] = GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE;
@@ -337,6 +347,49 @@ gtk_source_buffer_class_init (GtkSourceBufferClass *klass)
}
static void
+set_undo_manager (GtkSourceBuffer *buffer,
+ GtkSourceUndoManager *manager)
+{
+ if (manager == buffer->priv->undo_manager)
+ {
+ return;
+ }
+
+ if (buffer->priv->undo_manager != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (buffer->priv->undo_manager,
+ G_CALLBACK (gtk_source_buffer_can_undo_handler),
+ buffer);
+
+ g_signal_handlers_disconnect_by_func (buffer->priv->undo_manager,
+ G_CALLBACK (gtk_source_buffer_can_redo_handler),
+ buffer);
+
+ g_object_unref (buffer->priv->undo_manager);
+ buffer->priv->undo_manager = NULL;
+ }
+
+ if (manager != NULL)
+ {
+ buffer->priv->undo_manager = g_object_ref (manager);
+
+ g_signal_connect (buffer->priv->undo_manager,
+ "can-undo-changed",
+ G_CALLBACK (gtk_source_buffer_can_undo_handler),
+ buffer);
+
+ g_signal_connect (buffer->priv->undo_manager,
+ "can-redo-changed",
+ G_CALLBACK (gtk_source_buffer_can_redo_handler),
+ buffer);
+
+ /* Notify possible changes in the can-undo/redo state */
+ g_object_notify (G_OBJECT (buffer), "can-undo");
+ g_object_notify (G_OBJECT (buffer), "can-redo");
+ }
+}
+
+static void
gtk_source_buffer_init (GtkSourceBuffer *buffer)
{
GtkSourceBufferPrivate *priv;
@@ -346,27 +399,16 @@ gtk_source_buffer_init (GtkSourceBuffer *buffer)
buffer->priv = priv;
- priv->undo_manager = gtk_source_undo_manager_new (GTK_TEXT_BUFFER (buffer));
-
priv->highlight_syntax = TRUE;
priv->highlight_brackets = TRUE;
priv->bracket_mark = NULL;
priv->bracket_found = FALSE;
priv->source_marks = g_array_new (FALSE, FALSE, sizeof (GtkSourceMark *));
-
priv->style_scheme = _gtk_source_style_scheme_get_default ();
+
if (priv->style_scheme != NULL)
g_object_ref (priv->style_scheme);
-
- g_signal_connect (priv->undo_manager,
- "can_undo",
- G_CALLBACK (gtk_source_buffer_can_undo_handler),
- buffer);
- g_signal_connect (priv->undo_manager,
- "can_redo",
- G_CALLBACK (gtk_source_buffer_can_redo_handler),
- buffer);
}
static GObject *
@@ -375,13 +417,21 @@ gtk_source_buffer_constructor (GType type,
GObjectConstructParam *construct_param)
{
GObject *object;
+ GtkSourceBuffer *buffer;
object = G_OBJECT_CLASS(gtk_source_buffer_parent_class)->constructor (type,
n_construct_properties,
construct_param);
/* we need to know that the tag-table was set */
- GTK_SOURCE_BUFFER (object)->priv->constructed = TRUE;
+ buffer = GTK_SOURCE_BUFFER (object);
+ buffer->priv->constructed = TRUE;
+
+ if (buffer->priv->undo_manager == NULL)
+ {
+ /* This will install the default undo manager */
+ gtk_source_buffer_set_undo_manager (buffer, NULL);
+ }
return object;
}
@@ -416,8 +466,7 @@ gtk_source_buffer_dispose (GObject *object)
if (buffer->priv->undo_manager != NULL)
{
- g_object_unref (buffer->priv->undo_manager);
- buffer->priv->undo_manager = NULL;
+ set_undo_manager (buffer, NULL);
}
if (buffer->priv->highlight_engine != NULL)
@@ -481,6 +530,11 @@ gtk_source_buffer_set_property (GObject *object,
g_value_get_object (value));
break;
+ case PROP_UNDO_MANAGER:
+ gtk_source_buffer_set_undo_manager (source_buffer,
+ g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -513,7 +567,7 @@ gtk_source_buffer_get_property (GObject *object,
case PROP_MAX_UNDO_LEVELS:
g_value_set_int (value,
- gtk_source_buffer_get_max_undo_levels (source_buffer));
+ source_buffer->priv->max_undo_levels);
break;
case PROP_LANGUAGE:
@@ -532,6 +586,10 @@ gtk_source_buffer_get_property (GObject *object,
g_value_set_boolean (value, gtk_source_buffer_can_redo (source_buffer));
break;
+ case PROP_UNDO_MANAGER:
+ g_value_set_object (value, source_buffer->priv->undo_manager);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -580,9 +638,8 @@ gtk_source_buffer_new_with_language (GtkSourceLanguage *language)
}
static void
-gtk_source_buffer_can_undo_handler (GtkSourceUndoManager *um,
- gboolean can_undo,
- GtkSourceBuffer *buffer)
+gtk_source_buffer_can_undo_handler (GtkSourceUndoManager *manager,
+ GtkSourceBuffer *buffer)
{
g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
@@ -590,9 +647,8 @@ gtk_source_buffer_can_undo_handler (GtkSourceUndoManager *um,
}
static void
-gtk_source_buffer_can_redo_handler (GtkSourceUndoManager *um,
- gboolean can_redo,
- GtkSourceBuffer *buffer)
+gtk_source_buffer_can_redo_handler (GtkSourceUndoManager *manager,
+ GtkSourceBuffer *buffer)
{
g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
@@ -1106,7 +1162,7 @@ gtk_source_buffer_get_max_undo_levels (GtkSourceBuffer *buffer)
{
g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), 0);
- return gtk_source_undo_manager_get_max_undo_levels (buffer->priv->undo_manager);
+ return buffer->priv->max_undo_levels;
}
/**
@@ -1133,13 +1189,20 @@ gtk_source_buffer_set_max_undo_levels (GtkSourceBuffer *buffer,
{
g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
- if (gtk_source_undo_manager_get_max_undo_levels (
- buffer->priv->undo_manager) != max_undo_levels)
+ if (buffer->priv->max_undo_levels == max_undo_levels)
{
- gtk_source_undo_manager_set_max_undo_levels (buffer->priv->undo_manager,
- max_undo_levels);
- g_object_notify (G_OBJECT (buffer), "max-undo-levels");
+ return;
}
+
+ buffer->priv->max_undo_levels = max_undo_levels;
+
+ if (GTK_IS_SOURCE_UNDO_MANAGER_DEFAULT (buffer->priv->undo_manager))
+ {
+ gtk_source_undo_manager_default_set_max_undo_levels (GTK_SOURCE_UNDO_MANAGER_DEFAULT (buffer->priv->undo_manager),
+ max_undo_levels);
+ }
+
+ g_object_notify (G_OBJECT (buffer), "max-undo-levels");
}
/**
@@ -2194,3 +2257,53 @@ gtk_source_buffer_iter_backward_to_context_class_toggle (GtkSourceBuffer *buffer
}
}
+/**
+ * gtk_source_buffer_set_undo_manager:
+ * @buffer: A #GtkSourceBuffer
+ * @manager: A #GtkSourceUndoManager
+ *
+ * Set the buffer undo manager. If @manager is %NULL the default undo manager
+ * will be set.
+ *
+ **/
+void
+gtk_source_buffer_set_undo_manager (GtkSourceBuffer *buffer,
+ GtkSourceUndoManager *manager)
+{
+ g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
+ g_return_if_fail (manager == NULL || GTK_IS_SOURCE_UNDO_MANAGER (manager));
+
+ if (manager == NULL)
+ {
+ manager = g_object_new (GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT,
+ "buffer", buffer,
+ "max-undo-levels", buffer->priv->max_undo_levels,
+ NULL);
+ }
+ else
+ {
+ g_object_ref (manager);
+ }
+
+ set_undo_manager (buffer, manager);
+ g_object_unref (manager);
+
+ g_object_notify (G_OBJECT (buffer), "undo-manager");
+}
+
+/**
+ * gtk_source_buffer_get_undo_manager:
+ * @buffer: A #GtkSourceBuffer
+ *
+ * Get the undo manager associated with the buffer.
+ *
+ * Returns: A #GtkSourceUndoManager
+ *
+ **/
+GtkSourceUndoManager *
+gtk_source_buffer_get_undo_manager (GtkSourceBuffer *buffer)
+{
+ g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+
+ return buffer->priv->undo_manager;
+}
diff --git a/gtksourceview/gtksourcebuffer.h b/gtksourceview/gtksourcebuffer.h
index d2801a6..8d62949 100644
--- a/gtksourceview/gtksourcebuffer.h
+++ b/gtksourceview/gtksourcebuffer.h
@@ -30,6 +30,7 @@
#include <gtksourceview/gtksourcelanguage.h>
#include <gtksourceview/gtksourcemark.h>
#include <gtksourceview/gtksourcestylescheme.h>
+#include <gtksourceview/gtksourceundomanager.h>
G_BEGIN_DECLS
@@ -156,6 +157,10 @@ gboolean gtk_source_buffer_iter_backward_to_context_class_toggle
GtkTextIter *iter,
const gchar *context_class);
+GtkSourceUndoManager *gtk_source_buffer_get_undo_manager (GtkSourceBuffer *buffer);
+void gtk_source_buffer_set_undo_manager (GtkSourceBuffer *buffer,
+ GtkSourceUndoManager *manager);
+
/* private */
void _gtk_source_buffer_update_highlight (GtkSourceBuffer *buffer,
const GtkTextIter *start,
diff --git a/gtksourceview/gtksourceundomanager.c b/gtksourceview/gtksourceundomanager.c
index 4a29f87..1c16e8a 100644
--- a/gtksourceview/gtksourceundomanager.c
+++ b/gtksourceview/gtksourceundomanager.c
@@ -33,1094 +33,255 @@
#include "gtksourceundomanager.h"
#include "gtksourceview-marshal.h"
-
-#define DEFAULT_MAX_UNDO_LEVELS -1
-
-/*
- * The old code which used a GSList and g_slist_nth_element
- * was way too slow for many operations (search/replace, hit Ctrl-Z,
- * see gedit freezes). GSList was replaced with a GPtrArray with
- * minimal changes to the code: to avoid insertions at the beginning
- * of the array it uses array beginning as the list end and vice versa;
- * hence bunch of ugly action_list_* functions.
- * FIXME: so, somebody please rewrite this stuff using some nice data
- * structures or something.
- */
-
-typedef struct _GtkSourceUndoAction GtkSourceUndoAction;
-typedef struct _GtkSourceUndoInsertAction GtkSourceUndoInsertAction;
-typedef struct _GtkSourceUndoDeleteAction GtkSourceUndoDeleteAction;
-
-typedef enum {
- GTK_SOURCE_UNDO_ACTION_INSERT,
- GTK_SOURCE_UNDO_ACTION_DELETE
-} GtkSourceUndoActionType;
-
-/*
- * We use offsets instead of GtkTextIters because the last ones
- * require to much memory in this context without giving us any advantage.
- */
-
-struct _GtkSourceUndoInsertAction
-{
- gint pos;
- gchar *text;
- gint length;
- gint chars;
-};
-
-struct _GtkSourceUndoDeleteAction
-{
- gint start;
- gint end;
- gchar *text;
- gboolean forward;
-};
-
-struct _GtkSourceUndoAction
-{
- GtkSourceUndoActionType action_type;
-
- union {
- GtkSourceUndoInsertAction insert;
- GtkSourceUndoDeleteAction delete;
- } action;
-
- gint order_in_group;
-
- /* It is TRUE whether the action can be merged with the following action. */
- guint mergeable : 1;
-
- /* It is TRUE whether the action is marked as "modified".
- * An action is marked as "modified" if it changed the
- * state of the buffer from "not modified" to "modified". Only the first
- * action of a group can be marked as modified.
- * There can be a single action marked as "modified" in the actions list.
- */
- guint modified : 1;
-};
-
-/* INVALID is a pointer to an invalid action */
-#define INVALID ((void *) "IA")
-
-struct _GtkSourceUndoManagerPrivate
+/* Signals */
+enum
{
- GtkTextBuffer *document;
-
- GPtrArray *actions;
- gint next_redo;
-
- gint actions_in_current_group;
-
- gint running_not_undoable_actions;
-
- gint num_of_groups;
-
- gint max_undo_levels;
-
- guint can_undo : 1;
- guint can_redo : 1;
-
- /* It is TRUE whether, while undoing an action of the current group (with order_in_group > 1),
- * the state of the buffer changed from "not modified" to "modified".
- */
- guint modified_undoing_group : 1;
-
- /* Pointer to the action (in the action list) marked as "modified".
- * It is NULL when no action is marked as "modified".
- * It is INVALID when the action marked as "modified" has been removed
- * from the action list (freeing the list or resizing it) */
- GtkSourceUndoAction *modified_action;
-};
-
-enum {
- CAN_UNDO,
- CAN_REDO,
- LAST_SIGNAL
+ CAN_UNDO_CHANGED,
+ CAN_REDO_CHANGED,
+ NUM_SIGNALS
};
-G_DEFINE_TYPE (GtkSourceUndoManager, gtk_source_undo_manager, G_TYPE_OBJECT)
-
-static void gtk_source_undo_manager_finalize (GObject *object);
-
-static void gtk_source_undo_manager_insert_text_handler (GtkTextBuffer *buffer,
- GtkTextIter *pos,
- const gchar *text,
- gint length,
- GtkSourceUndoManager *um);
-static void gtk_source_undo_manager_delete_range_handler (GtkTextBuffer *buffer,
- GtkTextIter *start,
- GtkTextIter *end,
- GtkSourceUndoManager *um);
-static void gtk_source_undo_manager_begin_user_action_handler (GtkTextBuffer *buffer,
- GtkSourceUndoManager *um);
-static void gtk_source_undo_manager_modified_changed_handler (GtkTextBuffer *buffer,
- GtkSourceUndoManager *um);
-
-static void gtk_source_undo_manager_free_action_list (GtkSourceUndoManager *um);
-
-static void gtk_source_undo_manager_add_action (GtkSourceUndoManager *um,
- const GtkSourceUndoAction *undo_action);
-static void gtk_source_undo_manager_free_first_n_actions (GtkSourceUndoManager *um,
- gint n);
-static void gtk_source_undo_manager_check_list_size (GtkSourceUndoManager *um);
-
-static gboolean gtk_source_undo_manager_merge_action (GtkSourceUndoManager *um,
- const GtkSourceUndoAction *undo_action);
-
-static guint undo_manager_signals [LAST_SIGNAL] = { 0 };
-
-static void
-gtk_source_undo_manager_class_init (GtkSourceUndoManagerClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->finalize = gtk_source_undo_manager_finalize;
-
- klass->can_undo = NULL;
- klass->can_redo = NULL;
-
- undo_manager_signals[CAN_UNDO] =
- g_signal_new ("can_undo",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkSourceUndoManagerClass, can_undo),
- NULL, NULL,
- _gtksourceview_marshal_VOID__BOOLEAN,
- G_TYPE_NONE,
- 1,
- G_TYPE_BOOLEAN);
-
- undo_manager_signals[CAN_REDO] =
- g_signal_new ("can_redo",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkSourceUndoManagerClass, can_redo),
- NULL, NULL,
- _gtksourceview_marshal_VOID__BOOLEAN,
- G_TYPE_NONE,
- 1,
- G_TYPE_BOOLEAN);
-
- g_type_class_add_private (object_class, sizeof(GtkSourceUndoManagerPrivate));
-}
-
-static void
-gtk_source_undo_manager_init (GtkSourceUndoManager *um)
-{
- um->priv = G_TYPE_INSTANCE_GET_PRIVATE (um, GTK_TYPE_SOURCE_UNDO_MANAGER,
- GtkSourceUndoManagerPrivate);
-
- um->priv->actions = g_ptr_array_new ();
- um->priv->next_redo = 0;
-
- um->priv->can_undo = FALSE;
- um->priv->can_redo = FALSE;
-
- um->priv->running_not_undoable_actions = 0;
-
- um->priv->num_of_groups = 0;
-
- um->priv->max_undo_levels = DEFAULT_MAX_UNDO_LEVELS;
-
- um->priv->modified_action = NULL;
-
- um->priv->modified_undoing_group = FALSE;
-}
-
-static void
-gtk_source_undo_manager_finalize (GObject *object)
-{
- GtkSourceUndoManager *um;
-
- g_return_if_fail (object != NULL);
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (object));
-
- um = GTK_SOURCE_UNDO_MANAGER (object);
-
- g_return_if_fail (um->priv != NULL);
-
- gtk_source_undo_manager_free_action_list (um);
- g_ptr_array_free (um->priv->actions, TRUE);
-
- g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
- G_CALLBACK (gtk_source_undo_manager_delete_range_handler),
- um);
-
- g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
- G_CALLBACK (gtk_source_undo_manager_insert_text_handler),
- um);
-
- g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
- G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler),
- um);
+static guint signals[NUM_SIGNALS] = {0,};
- G_OBJECT_CLASS (gtk_source_undo_manager_parent_class)->finalize (object);
-}
-
-GtkSourceUndoManager*
-gtk_source_undo_manager_new (GtkTextBuffer* buffer)
-{
- GtkSourceUndoManager *um;
-
- um = GTK_SOURCE_UNDO_MANAGER (g_object_new (GTK_TYPE_SOURCE_UNDO_MANAGER, NULL));
-
- g_return_val_if_fail (um->priv != NULL, NULL);
- um->priv->document = buffer;
-
- g_signal_connect (G_OBJECT (buffer), "insert_text",
- G_CALLBACK (gtk_source_undo_manager_insert_text_handler),
- um);
-
- g_signal_connect (G_OBJECT (buffer), "delete_range",
- G_CALLBACK (gtk_source_undo_manager_delete_range_handler),
- um);
-
- g_signal_connect (G_OBJECT (buffer), "begin_user_action",
- G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler),
- um);
-
- g_signal_connect (G_OBJECT (buffer), "modified_changed",
- G_CALLBACK (gtk_source_undo_manager_modified_changed_handler),
- um);
- return um;
-}
-
-void
-gtk_source_undo_manager_begin_not_undoable_action (GtkSourceUndoManager *um)
-{
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- ++um->priv->running_not_undoable_actions;
-}
-
-static void
-gtk_source_undo_manager_end_not_undoable_action_internal (GtkSourceUndoManager *um)
-{
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- g_return_if_fail (um->priv->running_not_undoable_actions > 0);
-
- --um->priv->running_not_undoable_actions;
-}
-
-void
-gtk_source_undo_manager_end_not_undoable_action (GtkSourceUndoManager *um)
-{
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- gtk_source_undo_manager_end_not_undoable_action_internal (um);
-
- if (um->priv->running_not_undoable_actions == 0)
- {
- gtk_source_undo_manager_free_action_list (um);
-
- um->priv->next_redo = -1;
-
- if (um->priv->can_undo)
- {
- um->priv->can_undo = FALSE;
- g_signal_emit (G_OBJECT (um),
- undo_manager_signals [CAN_UNDO],
- 0,
- FALSE);
- }
-
- if (um->priv->can_redo)
- {
- um->priv->can_redo = FALSE;
- g_signal_emit (G_OBJECT (um),
- undo_manager_signals [CAN_REDO],
- 0,
- FALSE);
- }
- }
-}
-
-gboolean
-gtk_source_undo_manager_can_undo (const GtkSourceUndoManager *um)
+static gboolean
+gtk_source_undo_manager_can_undo_default (GtkSourceUndoManager *manager)
{
- g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
- g_return_val_if_fail (um->priv != NULL, FALSE);
-
- return um->priv->can_undo;
+ return FALSE;
}
-gboolean
-gtk_source_undo_manager_can_redo (const GtkSourceUndoManager *um)
+static gboolean
+gtk_source_undo_manager_can_redo_default (GtkSourceUndoManager *manager)
{
- g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
- g_return_val_if_fail (um->priv != NULL, FALSE);
-
- return um->priv->can_redo;
+ return FALSE;
}
static void
-set_cursor (GtkTextBuffer *buffer, gint cursor)
+gtk_source_undo_manager_undo_default (GtkSourceUndoManager *manager)
{
- GtkTextIter iter;
-
- /* Place the cursor at the requested position */
- gtk_text_buffer_get_iter_at_offset (buffer, &iter, cursor);
- gtk_text_buffer_place_cursor (buffer, &iter);
}
static void
-insert_text (GtkTextBuffer *buffer, gint pos, const gchar *text, gint len)
+gtk_source_undo_manager_redo_default (GtkSourceUndoManager *manager)
{
- GtkTextIter iter;
-
- gtk_text_buffer_get_iter_at_offset (buffer, &iter, pos);
- gtk_text_buffer_insert (buffer, &iter, text, len);
}
static void
-delete_text (GtkTextBuffer *buffer, gint start, gint end)
-{
- GtkTextIter start_iter;
- GtkTextIter end_iter;
-
- gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
-
- if (end < 0)
- gtk_text_buffer_get_end_iter (buffer, &end_iter);
- else
- gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
-
- gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
-}
-
-static gchar*
-get_chars (GtkTextBuffer *buffer, gint start, gint end)
-{
- GtkTextIter start_iter;
- GtkTextIter end_iter;
-
- gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
-
- if (end < 0)
- gtk_text_buffer_get_end_iter (buffer, &end_iter);
- else
- gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
-
- return gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, TRUE);
-}
-
-static GtkSourceUndoAction *
-action_list_nth_data (GPtrArray *array,
- gint n)
+gtk_source_undo_manager_begin_not_undoable_action_default (GtkSourceUndoManager *manager)
{
- if (n < 0 || n >= (gint)array->len)
- return NULL;
- else
- return array->pdata[array->len - 1 - n];
}
static void
-action_list_prepend (GPtrArray *array,
- GtkSourceUndoAction *action)
-{
- g_ptr_array_add (array, action);
-}
-
-static GtkSourceUndoAction *
-action_list_last_data (GPtrArray *array)
+gtk_source_undo_manager_end_not_undoable_action_default (GtkSourceUndoManager *manager)
{
- if (array->len != 0)
- return array->pdata[0];
- else
- return NULL;
}
static void
-action_list_delete_last (GPtrArray *array)
-{
- if (array->len != 0)
- {
- memmove (&array->pdata[0], &array->pdata[1], (array->len - 1)*sizeof (gpointer));
- g_ptr_array_set_size (array, array->len - 1);
- }
-}
-
-void
-gtk_source_undo_manager_undo (GtkSourceUndoManager *um)
+gtk_source_undo_manager_init (GtkSourceUndoManagerIface *iface)
{
- GtkSourceUndoAction *undo_action;
- gboolean modified = FALSE;
- gint cursor_pos = -1;
+ static gboolean initialized = FALSE;
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
- g_return_if_fail (um->priv->can_undo);
+ iface->can_undo = gtk_source_undo_manager_can_undo_default;
+ iface->can_redo = gtk_source_undo_manager_can_redo_default;
- um->priv->modified_undoing_group = FALSE;
+ iface->undo = gtk_source_undo_manager_undo_default;
+ iface->redo = gtk_source_undo_manager_redo_default;
- gtk_source_undo_manager_begin_not_undoable_action (um);
+ iface->begin_not_undoable_action = gtk_source_undo_manager_begin_not_undoable_action_default;
+ iface->end_not_undoable_action = gtk_source_undo_manager_end_not_undoable_action_default;
- do
+ if (!initialized)
{
- undo_action = action_list_nth_data (um->priv->actions, um->priv->next_redo + 1);
- g_return_if_fail (undo_action != NULL);
-
- /* undo_action->modified can be TRUE only if undo_action->order_in_group <= 1 */
- g_return_if_fail ((undo_action->order_in_group <= 1) ||
- ((undo_action->order_in_group > 1) && !undo_action->modified));
-
- if (undo_action->order_in_group <= 1)
- {
- /* Set modified to TRUE only if the buffer did not change its state from
- * "not modified" to "modified" undoing an action (with order_in_group > 1)
- * in current group. */
- modified = (undo_action->modified && !um->priv->modified_undoing_group);
- }
-
- switch (undo_action->action_type)
- {
- case GTK_SOURCE_UNDO_ACTION_DELETE:
- insert_text (
- um->priv->document,
- undo_action->action.delete.start,
- undo_action->action.delete.text,
- strlen (undo_action->action.delete.text));
-
- if (undo_action->action.delete.forward)
- cursor_pos = undo_action->action.delete.start;
- else
- cursor_pos = undo_action->action.delete.end;
-
- break;
-
- case GTK_SOURCE_UNDO_ACTION_INSERT:
- delete_text (
- um->priv->document,
- undo_action->action.insert.pos,
- undo_action->action.insert.pos +
- undo_action->action.insert.chars);
-
- cursor_pos = undo_action->action.insert.pos;
- break;
-
- default:
- /* Unknown action type. */
- g_return_if_reached ();
- }
-
- ++um->priv->next_redo;
-
- } while (undo_action->order_in_group > 1);
-
- if (cursor_pos >= 0)
- set_cursor (um->priv->document, cursor_pos);
-
- if (modified)
- {
- --um->priv->next_redo;
- gtk_text_buffer_set_modified (um->priv->document, FALSE);
- ++um->priv->next_redo;
- }
-
- gtk_source_undo_manager_end_not_undoable_action_internal (um);
-
- um->priv->modified_undoing_group = FALSE;
-
- if (!um->priv->can_redo)
- {
- um->priv->can_redo = TRUE;
- g_signal_emit (G_OBJECT (um),
- undo_manager_signals [CAN_REDO],
- 0,
- TRUE);
- }
+ /**
+ * GtkSourceUndoManager::can-undo-changed:
+ * @manager: The #GtkSourceUndoManager
+ *
+ * Emitted when the ability to undo has changed.
+ *
+ */
+ signals[CAN_UNDO_CHANGED] =
+ g_signal_new ("can-undo-changed",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkSourceUndoManagerIface, can_undo_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * GtkSourceUndoManager::can-redo-changed:
+ * @manager: The #GtkSourceUndoManager
+ *
+ * Emitted when the ability to redo has changed.
+ *
+ */
+ signals[CAN_REDO_CHANGED] =
+ g_signal_new ("can-redo-changed",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkSourceUndoManagerIface, can_redo_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
- if (um->priv->next_redo >= (gint)um->priv->actions->len - 1)
- {
- um->priv->can_undo = FALSE;
- g_signal_emit (G_OBJECT (um),
- undo_manager_signals [CAN_UNDO],
- 0,
- FALSE);
+ initialized = TRUE;
}
}
-void
-gtk_source_undo_manager_redo (GtkSourceUndoManager *um)
+GType
+gtk_source_undo_manager_get_type ()
{
- GtkSourceUndoAction *undo_action;
- gboolean modified = FALSE;
- gint cursor_pos = -1;
-
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
- g_return_if_fail (um->priv->can_redo);
-
- undo_action = action_list_nth_data (um->priv->actions, um->priv->next_redo);
- g_return_if_fail (undo_action != NULL);
-
- gtk_source_undo_manager_begin_not_undoable_action (um);
-
- do
+ static GType gtk_source_undo_manager_type_id = 0;
+
+ if (!gtk_source_undo_manager_type_id)
{
- if (undo_action->modified)
- {
- g_return_if_fail (undo_action->order_in_group <= 1);
- modified = TRUE;
- }
-
- --um->priv->next_redo;
-
- switch (undo_action->action_type)
+ static const GTypeInfo g_define_type_info =
{
- case GTK_SOURCE_UNDO_ACTION_DELETE:
- delete_text (
- um->priv->document,
- undo_action->action.delete.start,
- undo_action->action.delete.end);
-
- cursor_pos = undo_action->action.delete.start;
-
- break;
-
- case GTK_SOURCE_UNDO_ACTION_INSERT:
- cursor_pos = undo_action->action.insert.pos +
- undo_action->action.insert.length;
-
- insert_text (
- um->priv->document,
- undo_action->action.insert.pos,
- undo_action->action.insert.text,
- undo_action->action.insert.length);
-
- break;
-
- default:
- /* Unknown action type */
- ++um->priv->next_redo;
- g_return_if_reached ();
- }
-
- if (um->priv->next_redo < 0)
- undo_action = NULL;
- else
- undo_action = action_list_nth_data (um->priv->actions, um->priv->next_redo);
-
- } while ((undo_action != NULL) && (undo_action->order_in_group > 1));
-
- if (cursor_pos >= 0)
- set_cursor (um->priv->document, cursor_pos);
-
- if (modified)
- {
- ++um->priv->next_redo;
- gtk_text_buffer_set_modified (um->priv->document, FALSE);
- --um->priv->next_redo;
- }
-
- gtk_source_undo_manager_end_not_undoable_action_internal (um);
-
- if (um->priv->next_redo < 0)
- {
- um->priv->can_redo = FALSE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
- }
-
- if (!um->priv->can_undo)
- {
- um->priv->can_undo = TRUE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, TRUE);
- }
-}
-
-static void
-gtk_source_undo_action_free (GtkSourceUndoAction *action)
-{
- if (action == NULL)
- return;
-
- if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
- g_free (action->action.insert.text);
- else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
- g_free (action->action.delete.text);
- else
- g_return_if_reached ();
-
- g_free (action);
-}
-
-static void
-gtk_source_undo_manager_free_action_list (GtkSourceUndoManager *um)
-{
- gint i;
-
- for (i = (gint)um->priv->actions->len - 1; i >= 0; i--)
- {
- GtkSourceUndoAction *action = um->priv->actions->pdata[i];
-
- if (action->order_in_group == 1)
- --um->priv->num_of_groups;
-
- if (action->modified)
- um->priv->modified_action = INVALID;
-
- gtk_source_undo_action_free (action);
- }
-
- /* Some arbitrary limit, to avoid wasting space */
- if (um->priv->actions->len > 2048)
- {
- g_ptr_array_free (um->priv->actions, TRUE);
- um->priv->actions = g_ptr_array_new ();
- }
- else
- {
- g_ptr_array_set_size (um->priv->actions, 0);
+ sizeof (GtkSourceUndoManagerIface),
+ (GBaseInitFunc) gtk_source_undo_manager_init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ NULL
+ };
+
+ gtk_source_undo_manager_type_id =
+ g_type_register_static (G_TYPE_INTERFACE,
+ "GtkSourceUndoManager",
+ &g_define_type_info,
+ 0);
+
+ g_type_interface_add_prerequisite (gtk_source_undo_manager_type_id,
+ G_TYPE_OBJECT);
}
+
+ return gtk_source_undo_manager_type_id;
}
-static void
-gtk_source_undo_manager_insert_text_handler (GtkTextBuffer *buffer,
- GtkTextIter *pos,
- const gchar *text,
- gint length,
- GtkSourceUndoManager *um)
-{
- GtkSourceUndoAction undo_action;
-
- if (um->priv->running_not_undoable_actions > 0)
- return;
-
- undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT;
-
- undo_action.action.insert.pos = gtk_text_iter_get_offset (pos);
- undo_action.action.insert.text = (gchar*) text;
- undo_action.action.insert.length = length;
- undo_action.action.insert.chars = g_utf8_strlen (text, length);
-
- if ((undo_action.action.insert.chars > 1) || (g_utf8_get_char (text) == '\n'))
-
- undo_action.mergeable = FALSE;
- else
- undo_action.mergeable = TRUE;
-
- undo_action.modified = FALSE;
-
- gtk_source_undo_manager_add_action (um, &undo_action);
-}
-
-static void
-gtk_source_undo_manager_delete_range_handler (GtkTextBuffer *buffer,
- GtkTextIter *start,
- GtkTextIter *end,
- GtkSourceUndoManager *um)
+/**
+ * gtk_source_undo_manager_can_undo:
+ * @manager: A #GtkSourceUndoManager
+ *
+ * Get whether there are undo operations available.
+ *
+ * Returns: %TRUE if there are undo operations available, %FALSE otherwise
+ */
+gboolean
+gtk_source_undo_manager_can_undo (GtkSourceUndoManager *manager)
{
- GtkSourceUndoAction undo_action;
- GtkTextIter insert_iter;
-
- if (um->priv->running_not_undoable_actions > 0)
- return;
-
- undo_action.action_type = GTK_SOURCE_UNDO_ACTION_DELETE;
-
- gtk_text_iter_order (start, end);
-
- undo_action.action.delete.start = gtk_text_iter_get_offset (start);
- undo_action.action.delete.end = gtk_text_iter_get_offset (end);
-
- undo_action.action.delete.text = get_chars (
- buffer,
- undo_action.action.delete.start,
- undo_action.action.delete.end);
-
- /* figure out if the user used the Delete or the Backspace key */
- gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter,
- gtk_text_buffer_get_insert (buffer));
- if (gtk_text_iter_get_offset (&insert_iter) <= undo_action.action.delete.start)
- undo_action.action.delete.forward = TRUE;
- else
- undo_action.action.delete.forward = FALSE;
-
- if (((undo_action.action.delete.end - undo_action.action.delete.start) > 1) ||
- (g_utf8_get_char (undo_action.action.delete.text ) == '\n'))
- undo_action.mergeable = FALSE;
- else
- undo_action.mergeable = TRUE;
-
- undo_action.modified = FALSE;
-
- gtk_source_undo_manager_add_action (um, &undo_action);
-
- g_free (undo_action.action.delete.text);
-
+ g_return_val_if_fail (GTK_IS_SOURCE_UNDO_MANAGER (manager), FALSE);
+ return GTK_SOURCE_UNDO_MANAGER_GET_INTERFACE (manager)->can_undo (manager);
}
-static void
-gtk_source_undo_manager_begin_user_action_handler (GtkTextBuffer *buffer, GtkSourceUndoManager *um)
+/**
+ * gtk_source_undo_manager_can_redo:
+ * @manager: A #GtkSourceUndoManager
+ *
+ * Get whether there are redo operations available.
+ *
+ * Returns: %TRUE if there are redo operations available, %FALSE otherwise
+ */
+gboolean
+gtk_source_undo_manager_can_redo (GtkSourceUndoManager *manager)
{
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- if (um->priv->running_not_undoable_actions > 0)
- return;
-
- um->priv->actions_in_current_group = 0;
+ g_return_val_if_fail (GTK_IS_SOURCE_UNDO_MANAGER (manager), FALSE);
+ return GTK_SOURCE_UNDO_MANAGER_GET_INTERFACE (manager)->can_redo (manager);
}
-static void
-gtk_source_undo_manager_add_action (GtkSourceUndoManager *um,
- const GtkSourceUndoAction *undo_action)
+/**
+ * gtk_source_undo_manager_undo:
+ * @manager: A #GtkSourceUndoManager
+ *
+ * Perform a single undo. Calling this function when there are no undo operations
+ * available is an error. Use #gtk_source_undo_manager_can_undo to find out
+ * if there are undo operations available.
+ *
+ */
+void
+gtk_source_undo_manager_undo (GtkSourceUndoManager *manager)
{
- GtkSourceUndoAction* action;
-
- if (um->priv->next_redo >= 0)
- {
- gtk_source_undo_manager_free_first_n_actions (um, um->priv->next_redo + 1);
- }
-
- um->priv->next_redo = -1;
-
- if (!gtk_source_undo_manager_merge_action (um, undo_action))
- {
- action = g_new (GtkSourceUndoAction, 1);
- *action = *undo_action;
-
- if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
- action->action.insert.text = g_strndup (undo_action->action.insert.text, undo_action->action.insert.length);
- else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
- action->action.delete.text = g_strdup (undo_action->action.delete.text);
- else
- {
- g_free (action);
- g_return_if_reached ();
- }
-
- ++um->priv->actions_in_current_group;
- action->order_in_group = um->priv->actions_in_current_group;
-
- if (action->order_in_group == 1)
- ++um->priv->num_of_groups;
-
- action_list_prepend (um->priv->actions, action);
- }
-
- gtk_source_undo_manager_check_list_size (um);
-
- if (!um->priv->can_undo)
- {
- um->priv->can_undo = TRUE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, TRUE);
- }
-
- if (um->priv->can_redo)
- {
- um->priv->can_redo = FALSE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
- }
+ g_return_if_fail (GTK_IS_SOURCE_UNDO_MANAGER (manager));
+ GTK_SOURCE_UNDO_MANAGER_GET_INTERFACE (manager)->undo (manager);
}
-static void
-gtk_source_undo_manager_free_first_n_actions (GtkSourceUndoManager *um,
- gint n)
+/**
+ * gtk_source_undo_manager_redo:
+ * @manager: A #GtkSourceUndoManager
+ *
+ * Perform a single redo. Calling this function when there are no redo operations
+ * available is an error. Use #gtk_source_undo_manager_can_redo to find out
+ * if there are redo operations available.
+ *
+ */
+void
+gtk_source_undo_manager_redo (GtkSourceUndoManager *manager)
{
- gint i;
-
- if (um->priv->actions->len == 0)
- return;
-
- for (i = 0; i < n; i++)
- {
- GtkSourceUndoAction *action = um->priv->actions->pdata[um->priv->actions->len - 1];
-
- if (action->order_in_group == 1)
- --um->priv->num_of_groups;
-
- if (action->modified)
- um->priv->modified_action = INVALID;
-
- gtk_source_undo_action_free (action);
-
- g_ptr_array_set_size (um->priv->actions, um->priv->actions->len - 1);
-
- if (um->priv->actions->len == 0)
- return;
- }
-}
-
-static void
-gtk_source_undo_manager_check_list_size (GtkSourceUndoManager *um)
-{
- gint undo_levels;
-
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- undo_levels = gtk_source_undo_manager_get_max_undo_levels (um);
-
- if (undo_levels < 1)
- return;
-
- if (um->priv->num_of_groups > undo_levels)
- {
- GtkSourceUndoAction *undo_action;
-
- undo_action = action_list_last_data (um->priv->actions);
-
- do
- {
- if (undo_action->order_in_group == 1)
- --um->priv->num_of_groups;
-
- if (undo_action->modified)
- um->priv->modified_action = INVALID;
-
- gtk_source_undo_action_free (undo_action);
-
- action_list_delete_last (um->priv->actions);
-
- undo_action = action_list_last_data (um->priv->actions);
- g_return_if_fail (undo_action != NULL);
-
- } while ((undo_action->order_in_group > 1) ||
- (um->priv->num_of_groups > undo_levels));
- }
+ g_return_if_fail (GTK_IS_SOURCE_UNDO_MANAGER (manager));
+ GTK_SOURCE_UNDO_MANAGER_GET_INTERFACE (manager)->redo (manager);
}
/**
- * gtk_source_undo_manager_merge_action:
- * @um: a #GtkSourceUndoManager.
- * @undo_action: a #GtkSourceUndoAction.
+ * gtk_source_undo_manager_begin_not_undoable_action:
+ * @manager: A #GtkSourceUndoManager
*
- * This function tries to merge the undo action at the top of
- * the stack with a new undo action. So when we undo for example
- * typing, we can undo the whole word and not each letter by itself.
+ * Begin a not undoable action on the buffer. All changes between this call
+ * and the call to #gtk_source_undo_manager_end_not_undoable_action cannot
+ * be undone. This function should be re-entrant.
*
- * Return Value: %TRUE is merge was sucessful, %FALSE otherwise.
- **/
-static gboolean
-gtk_source_undo_manager_merge_action (GtkSourceUndoManager *um,
- const GtkSourceUndoAction *undo_action)
+ */
+void
+gtk_source_undo_manager_begin_not_undoable_action (GtkSourceUndoManager *manager)
{
- GtkSourceUndoAction *last_action;
-
- g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
- g_return_val_if_fail (um->priv != NULL, FALSE);
-
- if (um->priv->actions->len == 0)
- return FALSE;
-
- last_action = action_list_nth_data (um->priv->actions, 0);
-
- if (!last_action->mergeable)
- return FALSE;
-
- if ((!undo_action->mergeable) ||
- (undo_action->action_type != last_action->action_type))
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
- {
- if ((last_action->action.delete.forward != undo_action->action.delete.forward) ||
- ((last_action->action.delete.start != undo_action->action.delete.start) &&
- (last_action->action.delete.start != undo_action->action.delete.end)))
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- if (last_action->action.delete.start == undo_action->action.delete.start)
- {
- gchar *str;
-
-#define L (last_action->action.delete.end - last_action->action.delete.start - 1)
-#define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i)))
-
- /* Deleted with the delete key */
- if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
- (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
- ((g_utf8_get_char_at (last_action->action.delete.text, L) == ' ') ||
- (g_utf8_get_char_at (last_action->action.delete.text, L) == '\t')))
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- str = g_strdup_printf ("%s%s", last_action->action.delete.text,
- undo_action->action.delete.text);
-
- g_free (last_action->action.delete.text);
- last_action->action.delete.end += (undo_action->action.delete.end -
- undo_action->action.delete.start);
- last_action->action.delete.text = str;
- }
- else
- {
- gchar *str;
-
- /* Deleted with the backspace key */
- if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
- (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
- ((g_utf8_get_char (last_action->action.delete.text) == ' ') ||
- (g_utf8_get_char (last_action->action.delete.text) == '\t')))
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- str = g_strdup_printf ("%s%s", undo_action->action.delete.text,
- last_action->action.delete.text);
-
- g_free (last_action->action.delete.text);
- last_action->action.delete.start = undo_action->action.delete.start;
- last_action->action.delete.text = str;
- }
- }
- else if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
- {
- gchar* str;
-
-#define I (last_action->action.insert.chars - 1)
-
- if ((undo_action->action.insert.pos !=
- (last_action->action.insert.pos + last_action->action.insert.chars)) ||
- ((g_utf8_get_char (undo_action->action.insert.text) != ' ') &&
- (g_utf8_get_char (undo_action->action.insert.text) != '\t') &&
- ((g_utf8_get_char_at (last_action->action.insert.text, I) == ' ') ||
- (g_utf8_get_char_at (last_action->action.insert.text, I) == '\t')))
- )
- {
- last_action->mergeable = FALSE;
- return FALSE;
- }
-
- str = g_strdup_printf ("%s%s", last_action->action.insert.text,
- undo_action->action.insert.text);
-
- g_free (last_action->action.insert.text);
- last_action->action.insert.length += undo_action->action.insert.length;
- last_action->action.insert.text = str;
- last_action->action.insert.chars += undo_action->action.insert.chars;
-
- }
- else
- /* Unknown action inside undo merge encountered */
- g_return_val_if_reached (TRUE);
-
- return TRUE;
+ g_return_if_fail (GTK_IS_SOURCE_UNDO_MANAGER (manager));
+ GTK_SOURCE_UNDO_MANAGER_GET_INTERFACE (manager)->begin_not_undoable_action (manager);
}
-gint
-gtk_source_undo_manager_get_max_undo_levels (GtkSourceUndoManager *um)
+/**
+ * gtk_source_undo_manager_end_not_undoable_action:
+ * @manager: A #GtkSourceUndoManager
+ *
+ * Ends a not undoable action on the buffer.
+ *
+ */
+void
+gtk_source_undo_manager_end_not_undoable_action (GtkSourceUndoManager *manager)
{
- g_return_val_if_fail (um != NULL, 0);
- g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), 0);
-
- return um->priv->max_undo_levels;
+ g_return_if_fail (GTK_IS_SOURCE_UNDO_MANAGER (manager));
+ GTK_SOURCE_UNDO_MANAGER_GET_INTERFACE (manager)->end_not_undoable_action (manager);
}
+/**
+ * gtk_source_undo_manager_can_undo_changed:
+ * @manager: A #GtkSourceUndoManager
+ *
+ * Emits the ::can-undo-changed signal.
+ *
+ **/
void
-gtk_source_undo_manager_set_max_undo_levels (GtkSourceUndoManager *um,
- gint max_undo_levels)
+gtk_source_undo_manager_can_undo_changed (GtkSourceUndoManager *manager)
{
- gint old_levels;
-
- g_return_if_fail (um != NULL);
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
-
- old_levels = um->priv->max_undo_levels;
- um->priv->max_undo_levels = max_undo_levels;
-
- if (max_undo_levels < 1)
- return;
+ g_return_if_fail (GTK_IS_SOURCE_UNDO_MANAGER (manager));
- if (old_levels > max_undo_levels)
- {
- /* strip redo actions first */
- while (um->priv->next_redo >= 0 && (um->priv->num_of_groups > max_undo_levels))
- {
- gtk_source_undo_manager_free_first_n_actions (um, 1);
- um->priv->next_redo--;
- }
-
- /* now remove undo actions if necessary */
- gtk_source_undo_manager_check_list_size (um);
-
- /* emit "can_undo" and/or "can_redo" if appropiate */
- if (um->priv->next_redo < 0 && um->priv->can_redo)
- {
- um->priv->can_redo = FALSE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
- }
-
- if (um->priv->can_undo &&
- um->priv->next_redo >= (gint)um->priv->actions->len - 1)
- {
- um->priv->can_undo = FALSE;
- g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, FALSE);
- }
- }
+ g_signal_emit (manager, signals[CAN_UNDO_CHANGED], 0);
}
-static void
-gtk_source_undo_manager_modified_changed_handler (GtkTextBuffer *buffer,
- GtkSourceUndoManager *um)
+/**
+ * gtk_source_undo_manager_can_redo_changed:
+ * @manager: A #GtkSourceUndoManager
+ *
+ * Emits the ::can-redo-changed signal.
+ *
+ **/
+void
+gtk_source_undo_manager_can_redo_changed (GtkSourceUndoManager *manager)
{
- GtkSourceUndoAction *action;
- gint idx;
-
- g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
- g_return_if_fail (um->priv != NULL);
-
- if (um->priv->actions->len == 0)
- return;
-
- idx = um->priv->next_redo + 1;
- action = action_list_nth_data (um->priv->actions, idx);
-
- if (gtk_text_buffer_get_modified (buffer) == FALSE)
- {
- if (action != NULL)
- action->mergeable = FALSE;
-
- if (um->priv->modified_action != NULL)
- {
- if (um->priv->modified_action != INVALID)
- um->priv->modified_action->modified = FALSE;
-
- um->priv->modified_action = NULL;
- }
-
- return;
- }
-
- if (action == NULL)
- {
- g_return_if_fail (um->priv->running_not_undoable_actions > 0);
-
- return;
- }
-
- /* gtk_text_buffer_get_modified (buffer) == TRUE */
-
-// g_return_if_fail (um->priv->modified_action == NULL);
- if (um->priv->modified_action != NULL)
- {
- g_message ("%s: oops", G_STRLOC);
- return;
- }
-
- if (action->order_in_group > 1)
- um->priv->modified_undoing_group = TRUE;
-
- while (action->order_in_group > 1)
- {
- action = action_list_nth_data (um->priv->actions, ++idx);
- g_return_if_fail (action != NULL);
- }
+ g_return_if_fail (GTK_IS_SOURCE_UNDO_MANAGER (manager));
- action->modified = TRUE;
- um->priv->modified_action = action;
+ g_signal_emit (manager, signals[CAN_REDO_CHANGED], 0);
}
diff --git a/gtksourceview/gtksourceundomanager.h b/gtksourceview/gtksourceundomanager.h
index 0d3e03c..175a468 100644
--- a/gtksourceview/gtksourceundomanager.h
+++ b/gtksourceview/gtksourceundomanager.h
@@ -25,56 +25,55 @@
#ifndef __GTK_SOURCE_UNDO_MANAGER_H__
#define __GTK_SOURCE_UNDO_MANAGER_H__
-#include <gtk/gtktextbuffer.h>
+#include <gtk/gtk.h>
-#define GTK_TYPE_SOURCE_UNDO_MANAGER (gtk_source_undo_manager_get_type ())
-#define GTK_SOURCE_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_SOURCE_UNDO_MANAGER, GtkSourceUndoManager))
-#define GTK_SOURCE_UNDO_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_SOURCE_UNDO_MANAGER, GtkSourceUndoManagerClass))
-#define GTK_SOURCE_IS_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_UNDO_MANAGER))
-#define GTK_SOURCE_IS_UNDO_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_UNDO_MANAGER))
-#define GTK_SOURCE_UNDO_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_UNDO_MANAGER, GtkSourceUndoManagerClass))
+G_BEGIN_DECLS
+#define GTK_TYPE_SOURCE_UNDO_MANAGER (gtk_source_undo_manager_get_type ())
+#define GTK_SOURCE_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_UNDO_MANAGER, GtkSourceUndoManager))
+#define GTK_IS_SOURCE_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_UNDO_MANAGER))
+#define GTK_SOURCE_UNDO_MANAGER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_SOURCE_UNDO_MANAGER, GtkSourceUndoManagerIface))
typedef struct _GtkSourceUndoManager GtkSourceUndoManager;
-typedef struct _GtkSourceUndoManagerClass GtkSourceUndoManagerClass;
-typedef struct _GtkSourceUndoManagerPrivate GtkSourceUndoManagerPrivate;
+typedef struct _GtkSourceUndoManagerIface GtkSourceUndoManagerIface;
-struct _GtkSourceUndoManager
+struct _GtkSourceUndoManagerIface
{
- GObject base;
+ GTypeInterface parent;
- GtkSourceUndoManagerPrivate *priv;
-};
+ /* Interface functions */
+ void (*set_buffer) (GtkSourceUndoManager *manager,
+ GtkTextBuffer *buffer);
-struct _GtkSourceUndoManagerClass
-{
- GObjectClass parent_class;
+ gboolean (*can_undo) (GtkSourceUndoManager *manager);
+ gboolean (*can_redo) (GtkSourceUndoManager *manager);
+
+ void (*undo) (GtkSourceUndoManager *manager);
+ void (*redo) (GtkSourceUndoManager *manager);
+
+ void (*begin_not_undoable_action) (GtkSourceUndoManager *manager);
+ void (*end_not_undoable_action) (GtkSourceUndoManager *manager);
/* Signals */
- void (*can_undo) (GtkSourceUndoManager *um, gboolean can_undo);
- void (*can_redo) (GtkSourceUndoManager *um, gboolean can_redo);
+ void (*can_undo_changed) (GtkSourceUndoManager *manager);
+ void (*can_redo_changed) (GtkSourceUndoManager *manager);
};
-GType gtk_source_undo_manager_get_type (void) G_GNUC_CONST;
+GType gtk_source_undo_manager_get_type (void) G_GNUC_CONST;
-GtkSourceUndoManager* gtk_source_undo_manager_new (GtkTextBuffer *buffer);
+gboolean gtk_source_undo_manager_can_undo (GtkSourceUndoManager *manager);
+gboolean gtk_source_undo_manager_can_redo (GtkSourceUndoManager *manager);
-gboolean gtk_source_undo_manager_can_undo (const GtkSourceUndoManager *um);
-gboolean gtk_source_undo_manager_can_redo (const GtkSourceUndoManager *um);
+void gtk_source_undo_manager_undo (GtkSourceUndoManager *manager);
+void gtk_source_undo_manager_redo (GtkSourceUndoManager *manager);
-void gtk_source_undo_manager_undo (GtkSourceUndoManager *um);
-void gtk_source_undo_manager_redo (GtkSourceUndoManager *um);
+void gtk_source_undo_manager_begin_not_undoable_action (GtkSourceUndoManager *manager);
+void gtk_source_undo_manager_end_not_undoable_action (GtkSourceUndoManager *manager);
-void gtk_source_undo_manager_begin_not_undoable_action
- (GtkSourceUndoManager *um);
-void gtk_source_undo_manager_end_not_undoable_action
- (GtkSourceUndoManager *um);
+void gtk_source_undo_manager_can_undo_changed (GtkSourceUndoManager *manager);
+void gtk_source_undo_manager_can_redo_changed (GtkSourceUndoManager *manager);
-gint gtk_source_undo_manager_get_max_undo_levels
- (GtkSourceUndoManager *um);
-void gtk_source_undo_manager_set_max_undo_levels
- (GtkSourceUndoManager *um,
- gint undo_levels);
+G_END_DECLS
#endif /* __GTK_SOURCE_UNDO_MANAGER_H__ */
diff --git a/gtksourceview/gtksourceundomanagerdefault.c b/gtksourceview/gtksourceundomanagerdefault.c
new file mode 100644
index 0000000..db7d03e
--- /dev/null
+++ b/gtksourceview/gtksourceundomanagerdefault.c
@@ -0,0 +1,1194 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gtksourceundomanager.c
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
+ * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
+ * Copyright (C) 2002-2005 Paolo Maggi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gtksourceundomanagerdefault.h"
+#include "gtksourceundomanager.h"
+#include "gtksourceview-i18n.h"
+
+#define DEFAULT_MAX_UNDO_LEVELS -1
+
+/*
+ * The old code which used a GSList and g_slist_nth_element
+ * was way too slow for many operations (search/replace, hit Ctrl-Z,
+ * see gedit freezes). GSList was replaced with a GPtrArray with
+ * minimal changes to the code: to avoid insertions at the beginning
+ * of the array it uses array beginning as the list end and vice versa;
+ * hence bunch of ugly action_list_* functions.
+ * FIXME: so, somebody please rewrite this stuff using some nice data
+ * structures or something.
+ */
+
+typedef struct _GtkSourceUndoAction GtkSourceUndoAction;
+typedef struct _GtkSourceUndoInsertAction GtkSourceUndoInsertAction;
+typedef struct _GtkSourceUndoDeleteAction GtkSourceUndoDeleteAction;
+
+typedef enum
+{
+ GTK_SOURCE_UNDO_ACTION_INSERT,
+ GTK_SOURCE_UNDO_ACTION_DELETE
+} GtkSourceUndoActionType;
+
+/*
+ * We use offsets instead of GtkTextIters because the last ones
+ * require to much memory in this context without giving us any advantage.
+ */
+
+struct _GtkSourceUndoInsertAction
+{
+ gint pos;
+ gchar *text;
+ gint length;
+ gint chars;
+};
+
+struct _GtkSourceUndoDeleteAction
+{
+ gint start;
+ gint end;
+ gchar *text;
+ gboolean forward;
+};
+
+struct _GtkSourceUndoAction
+{
+ GtkSourceUndoActionType action_type;
+
+ union
+ {
+ GtkSourceUndoInsertAction insert;
+ GtkSourceUndoDeleteAction delete;
+ } action;
+
+ gint order_in_group;
+
+ /* It is TRUE whether the action can be merged with the following action. */
+ guint mergeable : 1;
+
+ /* It is TRUE whether the action is marked as "modified".
+ * An action is marked as "modified" if it changed the
+ * state of the buffer from "not modified" to "modified". Only the first
+ * action of a group can be marked as modified.
+ * There can be a single action marked as "modified" in the actions list.
+ */
+ guint modified : 1;
+};
+
+/* INVALID is a pointer to an invalid action */
+#define INVALID ((void *) "IA")
+
+enum
+{
+ INSERT_TEXT,
+ DELETE_RANGE,
+ BEGIN_USER_ACTION,
+ MODIFIED_CHANGED,
+ NUM_SIGNALS
+};
+
+struct _GtkSourceUndoManagerDefaultPrivate
+{
+ GtkTextBuffer *buffer;
+
+ GPtrArray *actions;
+ gint next_redo;
+
+ gint actions_in_current_group;
+ gint running_not_undoable_actions;
+ gint num_of_groups;
+ gint max_undo_levels;
+
+ guint can_undo : 1;
+ guint can_redo : 1;
+
+ /* It is TRUE whether, while undoing an action of the current group (with order_in_group > 1),
+ * the state of the buffer changed from "not modified" to "modified".
+ */
+ guint modified_undoing_group : 1;
+
+ /* Pointer to the action (in the action list) marked as "modified".
+ * It is NULL when no action is marked as "modified".
+ * It is INVALID when the action marked as "modified" has been removed
+ * from the action list (freeing the list or resizing it) */
+ GtkSourceUndoAction *modified_action;
+
+ guint buffer_signals[NUM_SIGNALS];
+};
+
+/* Properties */
+enum
+{
+ PROP_0,
+ PROP_BUFFER,
+ PROP_MAX_UNDO_LEVELS
+};
+
+static void insert_text_handler (GtkTextBuffer *buffer,
+ GtkTextIter *pos,
+ const gchar *text,
+ gint length,
+ GtkSourceUndoManagerDefault *um);
+
+static void delete_range_handler (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ GtkSourceUndoManagerDefault *um);
+
+static void begin_user_action_handler (GtkTextBuffer *buffer,
+ GtkSourceUndoManagerDefault *um);
+
+static void modified_changed_handler (GtkTextBuffer *buffer,
+ GtkSourceUndoManagerDefault *um);
+
+static void free_action_list (GtkSourceUndoManagerDefault *um);
+
+static void add_action (GtkSourceUndoManagerDefault *um,
+ const GtkSourceUndoAction *undo_action);
+static void free_first_n_actions (GtkSourceUndoManagerDefault *um,
+ gint n);
+static void check_list_size (GtkSourceUndoManagerDefault *um);
+
+static gboolean merge_action (GtkSourceUndoManagerDefault *um,
+ const GtkSourceUndoAction *undo_action);
+
+static void gtk_source_undo_manager_iface_init (GtkSourceUndoManagerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkSourceUndoManagerDefault, gtk_source_undo_manager_default, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_SOURCE_UNDO_MANAGER,
+ gtk_source_undo_manager_iface_init))
+
+static void
+gtk_source_undo_manager_default_finalize (GObject *object)
+{
+ GtkSourceUndoManagerDefault *manager;
+
+ manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
+
+ free_action_list (manager);
+ g_ptr_array_free (manager->priv->actions, TRUE);
+
+ G_OBJECT_CLASS (gtk_source_undo_manager_default_parent_class)->finalize (object);
+}
+
+static void
+clear_undo (GtkSourceUndoManagerDefault *manager)
+{
+ free_action_list (manager);
+
+ manager->priv->next_redo = -1;
+
+ if (manager->priv->can_undo)
+ {
+ manager->priv->can_undo = FALSE;
+ gtk_source_undo_manager_can_undo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+ }
+
+ if (manager->priv->can_redo)
+ {
+ manager->priv->can_redo = FALSE;
+ gtk_source_undo_manager_can_redo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+ }
+}
+
+static void
+set_buffer (GtkSourceUndoManagerDefault *manager,
+ GtkTextBuffer *buffer)
+{
+ if (buffer == manager->priv->buffer)
+ {
+ return;
+ }
+
+ clear_undo (manager);
+
+ if (manager->priv->buffer != NULL)
+ {
+ gint i;
+
+ for (i = 0; i < NUM_SIGNALS; ++i)
+ {
+ g_signal_handler_disconnect (manager->priv->buffer,
+ manager->priv->buffer_signals[i]);
+ }
+
+ g_object_unref (manager->priv->buffer);
+ manager->priv->buffer = NULL;
+ }
+
+ if (buffer != NULL)
+ {
+ manager->priv->buffer = g_object_ref (buffer);
+
+ manager->priv->buffer_signals[INSERT_TEXT] =
+ g_signal_connect (buffer,
+ "insert-text",
+ G_CALLBACK (insert_text_handler),
+ manager);
+
+ manager->priv->buffer_signals[DELETE_RANGE] =
+ g_signal_connect (buffer,
+ "delete-range",
+ G_CALLBACK (delete_range_handler),
+ manager);
+
+ manager->priv->buffer_signals[BEGIN_USER_ACTION] =
+ g_signal_connect (buffer,
+ "begin-user-action",
+ G_CALLBACK (begin_user_action_handler),
+ manager);
+
+ manager->priv->buffer_signals[MODIFIED_CHANGED] =
+ g_signal_connect (buffer,
+ "modified-changed",
+ G_CALLBACK (modified_changed_handler),
+ manager);
+ }
+}
+
+static void
+set_max_undo_levels (GtkSourceUndoManagerDefault *manager,
+ gint max_undo_levels)
+{
+ gint old_levels;
+
+ old_levels = manager->priv->max_undo_levels;
+ manager->priv->max_undo_levels = max_undo_levels;
+
+ if (max_undo_levels < 1)
+ return;
+
+ if (old_levels > max_undo_levels)
+ {
+ /* strip redo actions first */
+ while (manager->priv->next_redo >= 0 &&
+ (manager->priv->num_of_groups > max_undo_levels))
+ {
+ free_first_n_actions (manager, 1);
+ manager->priv->next_redo--;
+ }
+
+ /* now remove undo actions if necessary */
+ check_list_size (manager);
+
+ /* emit "can_undo" and/or "can_redo" if appropiate */
+ if (manager->priv->next_redo < 0 && manager->priv->can_redo)
+ {
+ manager->priv->can_redo = FALSE;
+ gtk_source_undo_manager_can_redo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+ }
+
+ if (manager->priv->can_undo &&
+ manager->priv->next_redo >= (gint)manager->priv->actions->len - 1)
+ {
+ manager->priv->can_undo = FALSE;
+ gtk_source_undo_manager_can_undo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+ }
+ }
+}
+
+static void
+gtk_source_undo_manager_default_dispose (GObject *object)
+{
+ GtkSourceUndoManagerDefault *manager;
+
+ manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
+
+ if (manager->priv->buffer != NULL)
+ {
+ /* Clear the buffer */
+ set_buffer (manager, NULL);
+ }
+}
+
+static void
+gtk_source_undo_manager_default_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSourceUndoManagerDefault *self = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
+
+ switch (prop_id)
+ {
+ case PROP_BUFFER:
+ set_buffer (self, g_value_get_object (value));
+ break;
+ case PROP_MAX_UNDO_LEVELS:
+ gtk_source_undo_manager_default_set_max_undo_levels (self,
+ g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_source_undo_manager_default_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSourceUndoManagerDefault *self = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
+
+ switch (prop_id)
+ {
+ case PROP_BUFFER:
+ g_value_set_object (value, self->priv->buffer);
+ break;
+ case PROP_MAX_UNDO_LEVELS:
+ g_value_set_int (value, self->priv->max_undo_levels);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_source_undo_manager_default_class_init (GtkSourceUndoManagerDefaultClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_source_undo_manager_default_finalize;
+ object_class->dispose = gtk_source_undo_manager_default_dispose;
+
+ object_class->set_property = gtk_source_undo_manager_default_set_property;
+ object_class->get_property = gtk_source_undo_manager_default_get_property;
+
+ g_object_class_install_property (object_class,
+ PROP_BUFFER,
+ g_param_spec_object ("buffer",
+ _("Buffer"),
+ _("The text buffer to add undo support on"),
+ GTK_TYPE_TEXT_BUFFER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_MAX_UNDO_LEVELS,
+ g_param_spec_int ("max-undo-levels",
+ _("Maximum Undo Levels"),
+ _("Number of undo levels for "
+ "the buffer"),
+ -1,
+ G_MAXINT,
+ DEFAULT_MAX_UNDO_LEVELS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (object_class, sizeof(GtkSourceUndoManagerDefaultPrivate));
+}
+
+static void
+gtk_source_undo_manager_default_init (GtkSourceUndoManagerDefault *um)
+{
+ um->priv = G_TYPE_INSTANCE_GET_PRIVATE (um, GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT,
+ GtkSourceUndoManagerDefaultPrivate);
+
+ um->priv->actions = g_ptr_array_new ();
+}
+
+static void
+end_not_undoable_action_internal (GtkSourceUndoManagerDefault *manager)
+{
+ g_return_if_fail (manager->priv->running_not_undoable_actions > 0);
+
+ --manager->priv->running_not_undoable_actions;
+}
+
+/* Interface implementations */
+static void
+gtk_source_undo_manager_begin_not_undoable_action_impl (GtkSourceUndoManager *manager)
+{
+ GtkSourceUndoManagerDefault *manager_default;
+
+ manager_default = GTK_SOURCE_UNDO_MANAGER_DEFAULT (manager);
+ ++manager_default->priv->running_not_undoable_actions;
+}
+
+static void
+gtk_source_undo_manager_end_not_undoable_action_impl (GtkSourceUndoManager *manager)
+{
+ GtkSourceUndoManagerDefault *manager_default;
+
+ manager_default = GTK_SOURCE_UNDO_MANAGER_DEFAULT (manager);
+ end_not_undoable_action_internal (manager_default);
+
+ if (manager_default->priv->running_not_undoable_actions == 0)
+ {
+ clear_undo (manager_default);
+ }
+}
+
+static gboolean
+gtk_source_undo_manager_can_undo_impl (GtkSourceUndoManager *manager)
+{
+ return GTK_SOURCE_UNDO_MANAGER_DEFAULT (manager)->priv->can_undo;
+}
+
+static gboolean
+gtk_source_undo_manager_can_redo_impl (GtkSourceUndoManager *manager)
+{
+ return GTK_SOURCE_UNDO_MANAGER_DEFAULT (manager)->priv->can_redo;
+}
+
+static void
+set_cursor (GtkTextBuffer *buffer,
+ gint cursor)
+{
+ GtkTextIter iter;
+
+ /* Place the cursor at the requested position */
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter, cursor);
+ gtk_text_buffer_place_cursor (buffer, &iter);
+}
+
+static void
+insert_text (GtkTextBuffer *buffer,
+ gint pos,
+ const gchar *text,
+ gint len)
+{
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter, pos);
+ gtk_text_buffer_insert (buffer, &iter, text, len);
+}
+
+static void
+delete_text (GtkTextBuffer *buffer,
+ gint start,
+ gint end)
+{
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
+
+ if (end < 0)
+ gtk_text_buffer_get_end_iter (buffer, &end_iter);
+ else
+ gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
+
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+}
+
+static gchar *
+get_chars (GtkTextBuffer *buffer,
+ gint start,
+ gint end)
+{
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
+
+ if (end < 0)
+ gtk_text_buffer_get_end_iter (buffer, &end_iter);
+ else
+ gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
+
+ return gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, TRUE);
+}
+
+static GtkSourceUndoAction *
+action_list_nth_data (GPtrArray *array,
+ gint n)
+{
+ if (n < 0 || n >= (gint)array->len)
+ return NULL;
+ else
+ return array->pdata[array->len - 1 - n];
+}
+
+static void
+action_list_prepend (GPtrArray *array,
+ GtkSourceUndoAction *action)
+{
+ g_ptr_array_add (array, action);
+}
+
+static GtkSourceUndoAction *
+action_list_last_data (GPtrArray *array)
+{
+ if (array->len != 0)
+ return array->pdata[0];
+ else
+ return NULL;
+}
+
+static void
+action_list_delete_last (GPtrArray *array)
+{
+ if (array->len != 0)
+ {
+ memmove (&array->pdata[0], &array->pdata[1], (array->len - 1)*sizeof (gpointer));
+ g_ptr_array_set_size (array, array->len - 1);
+ }
+}
+
+static void
+gtk_source_undo_manager_undo_impl (GtkSourceUndoManager *manager)
+{
+ GtkSourceUndoManagerDefault *manager_default;
+ GtkSourceUndoAction *undo_action;
+ gboolean modified = FALSE;
+ gint cursor_pos = -1;
+
+ manager_default = GTK_SOURCE_UNDO_MANAGER_DEFAULT (manager);
+
+ g_return_if_fail (manager_default->priv->can_undo);
+
+ manager_default->priv->modified_undoing_group = FALSE;
+
+ gtk_source_undo_manager_begin_not_undoable_action (manager);
+
+ do
+ {
+ undo_action = action_list_nth_data (manager_default->priv->actions,
+ manager_default->priv->next_redo + 1);
+
+ g_return_if_fail (undo_action != NULL);
+
+ /* undo_action->modified can be TRUE only if undo_action->order_in_group <= 1 */
+ g_return_if_fail ((undo_action->order_in_group <= 1) ||
+ ((undo_action->order_in_group > 1) && !undo_action->modified));
+
+ if (undo_action->order_in_group <= 1)
+ {
+ /* Set modified to TRUE only if the buffer did not change its state from
+ * "not modified" to "modified" undoing an action (with order_in_group > 1)
+ * in current group. */
+ modified = (undo_action->modified &&
+ !manager_default->priv->modified_undoing_group);
+ }
+
+ switch (undo_action->action_type)
+ {
+ case GTK_SOURCE_UNDO_ACTION_DELETE:
+ insert_text (manager_default->priv->buffer,
+ undo_action->action.delete.start,
+ undo_action->action.delete.text,
+ strlen (undo_action->action.delete.text));
+
+ if (undo_action->action.delete.forward)
+ cursor_pos = undo_action->action.delete.start;
+ else
+ cursor_pos = undo_action->action.delete.end;
+
+ break;
+
+ case GTK_SOURCE_UNDO_ACTION_INSERT:
+ delete_text (manager_default->priv->buffer,
+ undo_action->action.insert.pos,
+ undo_action->action.insert.pos +
+ undo_action->action.insert.chars);
+
+ cursor_pos = undo_action->action.insert.pos;
+ break;
+
+ default:
+ /* Unknown action type. */
+ g_return_if_reached ();
+ }
+
+ ++manager_default->priv->next_redo;
+
+ } while (undo_action->order_in_group > 1);
+
+ if (cursor_pos >= 0)
+ set_cursor (manager_default->priv->buffer, cursor_pos);
+
+ if (modified)
+ {
+ --manager_default->priv->next_redo;
+ gtk_text_buffer_set_modified (manager_default->priv->buffer, FALSE);
+ ++manager_default->priv->next_redo;
+ }
+
+ /* FIXME: why does this call the internal one ? */
+ end_not_undoable_action_internal (manager_default);
+
+ manager_default->priv->modified_undoing_group = FALSE;
+
+ if (!manager_default->priv->can_redo)
+ {
+ manager_default->priv->can_redo = TRUE;
+ gtk_source_undo_manager_can_redo_changed (manager);
+ }
+
+ if (manager_default->priv->next_redo >= (gint)manager_default->priv->actions->len - 1)
+ {
+ manager_default->priv->can_undo = FALSE;
+ gtk_source_undo_manager_can_undo_changed (manager);
+ }
+}
+
+static void
+gtk_source_undo_manager_redo_impl (GtkSourceUndoManager *manager)
+{
+ GtkSourceUndoManagerDefault *manager_default;
+ GtkSourceUndoAction *undo_action;
+ gboolean modified = FALSE;
+ gint cursor_pos = -1;
+
+ manager_default = GTK_SOURCE_UNDO_MANAGER_DEFAULT (manager);
+
+ g_return_if_fail (manager_default->priv->can_redo);
+
+ undo_action = action_list_nth_data (manager_default->priv->actions,
+ manager_default->priv->next_redo);
+ g_return_if_fail (undo_action != NULL);
+
+ gtk_source_undo_manager_begin_not_undoable_action (manager);
+
+ do
+ {
+ if (undo_action->modified)
+ {
+ g_return_if_fail (undo_action->order_in_group <= 1);
+ modified = TRUE;
+ }
+
+ --manager_default->priv->next_redo;
+
+ switch (undo_action->action_type)
+ {
+ case GTK_SOURCE_UNDO_ACTION_DELETE:
+ delete_text (manager_default->priv->buffer,
+ undo_action->action.delete.start,
+ undo_action->action.delete.end);
+
+ cursor_pos = undo_action->action.delete.start;
+
+ break;
+
+ case GTK_SOURCE_UNDO_ACTION_INSERT:
+ cursor_pos = undo_action->action.insert.pos +
+ undo_action->action.insert.length;
+
+ insert_text (manager_default->priv->buffer,
+ undo_action->action.insert.pos,
+ undo_action->action.insert.text,
+ undo_action->action.insert.length);
+
+ break;
+
+ default:
+ /* Unknown action type */
+ ++manager_default->priv->next_redo;
+ g_return_if_reached ();
+ }
+
+ if (manager_default->priv->next_redo < 0)
+ undo_action = NULL;
+ else
+ undo_action = action_list_nth_data (manager_default->priv->actions,
+ manager_default->priv->next_redo);
+
+ } while ((undo_action != NULL) && (undo_action->order_in_group > 1));
+
+ if (cursor_pos >= 0)
+ set_cursor (manager_default->priv->buffer, cursor_pos);
+
+ if (modified)
+ {
+ ++manager_default->priv->next_redo;
+ gtk_text_buffer_set_modified (manager_default->priv->buffer, FALSE);
+ --manager_default->priv->next_redo;
+ }
+
+ /* FIXME: why is this only internal ?*/
+ end_not_undoable_action_internal (manager_default);
+
+ if (manager_default->priv->next_redo < 0)
+ {
+ manager_default->priv->can_redo = FALSE;
+ gtk_source_undo_manager_can_redo_changed (manager);
+ }
+
+ if (!manager_default->priv->can_undo)
+ {
+ manager_default->priv->can_undo = TRUE;
+ gtk_source_undo_manager_can_undo_changed (manager);
+ }
+}
+
+static void
+gtk_source_undo_action_free (GtkSourceUndoAction *action)
+{
+ if (action == NULL)
+ return;
+
+ if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
+ g_free (action->action.insert.text);
+ else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
+ g_free (action->action.delete.text);
+ else
+ g_return_if_reached ();
+
+ g_free (action);
+}
+
+static void
+free_action_list (GtkSourceUndoManagerDefault *um)
+{
+ gint i;
+
+ for (i = (gint)um->priv->actions->len - 1; i >= 0; i--)
+ {
+ GtkSourceUndoAction *action = um->priv->actions->pdata[i];
+
+ if (action->order_in_group == 1)
+ --um->priv->num_of_groups;
+
+ if (action->modified)
+ um->priv->modified_action = INVALID;
+
+ gtk_source_undo_action_free (action);
+ }
+
+ /* Some arbitrary limit, to avoid wasting space */
+ if (um->priv->actions->len > 2048)
+ {
+ g_ptr_array_free (um->priv->actions, TRUE);
+ um->priv->actions = g_ptr_array_new ();
+ }
+ else
+ {
+ g_ptr_array_set_size (um->priv->actions, 0);
+ }
+}
+
+static void
+insert_text_handler (GtkTextBuffer *buffer,
+ GtkTextIter *pos,
+ const gchar *text,
+ gint length,
+ GtkSourceUndoManagerDefault *manager)
+{
+ GtkSourceUndoAction undo_action;
+
+ if (manager->priv->running_not_undoable_actions > 0)
+ return;
+
+ undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT;
+
+ undo_action.action.insert.pos = gtk_text_iter_get_offset (pos);
+ undo_action.action.insert.text = (gchar*) text;
+ undo_action.action.insert.length = length;
+ undo_action.action.insert.chars = g_utf8_strlen (text, length);
+
+ if ((undo_action.action.insert.chars > 1) || (g_utf8_get_char (text) == '\n'))
+
+ undo_action.mergeable = FALSE;
+ else
+ undo_action.mergeable = TRUE;
+
+ undo_action.modified = FALSE;
+
+ add_action (manager, &undo_action);
+}
+
+static void
+delete_range_handler (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ GtkSourceUndoManagerDefault *um)
+{
+ GtkSourceUndoAction undo_action;
+ GtkTextIter insert_iter;
+
+ if (um->priv->running_not_undoable_actions > 0)
+ return;
+
+ undo_action.action_type = GTK_SOURCE_UNDO_ACTION_DELETE;
+
+ gtk_text_iter_order (start, end);
+
+ undo_action.action.delete.start = gtk_text_iter_get_offset (start);
+ undo_action.action.delete.end = gtk_text_iter_get_offset (end);
+
+ undo_action.action.delete.text = get_chars (
+ buffer,
+ undo_action.action.delete.start,
+ undo_action.action.delete.end);
+
+ /* figure out if the user used the Delete or the Backspace key */
+ gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter,
+ gtk_text_buffer_get_insert (buffer));
+ if (gtk_text_iter_get_offset (&insert_iter) <= undo_action.action.delete.start)
+ undo_action.action.delete.forward = TRUE;
+ else
+ undo_action.action.delete.forward = FALSE;
+
+ if (((undo_action.action.delete.end - undo_action.action.delete.start) > 1) ||
+ (g_utf8_get_char (undo_action.action.delete.text ) == '\n'))
+ undo_action.mergeable = FALSE;
+ else
+ undo_action.mergeable = TRUE;
+
+ undo_action.modified = FALSE;
+
+ add_action (um, &undo_action);
+
+ g_free (undo_action.action.delete.text);
+
+}
+
+static void
+begin_user_action_handler (GtkTextBuffer *buffer,
+ GtkSourceUndoManagerDefault *um)
+{
+ if (um->priv->running_not_undoable_actions > 0)
+ return;
+
+ um->priv->actions_in_current_group = 0;
+}
+
+static void
+add_action (GtkSourceUndoManagerDefault *um,
+ const GtkSourceUndoAction *undo_action)
+{
+ GtkSourceUndoAction* action;
+
+ if (um->priv->next_redo >= 0)
+ {
+ free_first_n_actions (um, um->priv->next_redo + 1);
+ }
+
+ um->priv->next_redo = -1;
+
+ if (!merge_action (um, undo_action))
+ {
+ action = g_new (GtkSourceUndoAction, 1);
+ *action = *undo_action;
+
+ if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
+ action->action.insert.text = g_strndup (undo_action->action.insert.text, undo_action->action.insert.length);
+ else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
+ action->action.delete.text = g_strdup (undo_action->action.delete.text);
+ else
+ {
+ g_free (action);
+ g_return_if_reached ();
+ }
+
+ ++um->priv->actions_in_current_group;
+ action->order_in_group = um->priv->actions_in_current_group;
+
+ if (action->order_in_group == 1)
+ ++um->priv->num_of_groups;
+
+ action_list_prepend (um->priv->actions, action);
+ }
+
+ check_list_size (um);
+
+ if (!um->priv->can_undo)
+ {
+ um->priv->can_undo = TRUE;
+ gtk_source_undo_manager_can_undo_changed (GTK_SOURCE_UNDO_MANAGER (um));
+ }
+
+ if (um->priv->can_redo)
+ {
+ um->priv->can_redo = FALSE;
+ gtk_source_undo_manager_can_redo_changed (GTK_SOURCE_UNDO_MANAGER (um));
+ }
+}
+
+static void
+free_first_n_actions (GtkSourceUndoManagerDefault *um,
+ gint n)
+{
+ gint i;
+
+ if (um->priv->actions->len == 0)
+ return;
+
+ for (i = 0; i < n; i++)
+ {
+ GtkSourceUndoAction *action = um->priv->actions->pdata[um->priv->actions->len - 1];
+
+ if (action->order_in_group == 1)
+ --um->priv->num_of_groups;
+
+ if (action->modified)
+ um->priv->modified_action = INVALID;
+
+ gtk_source_undo_action_free (action);
+
+ g_ptr_array_set_size (um->priv->actions, um->priv->actions->len - 1);
+
+ if (um->priv->actions->len == 0)
+ return;
+ }
+}
+
+static void
+check_list_size (GtkSourceUndoManagerDefault *um)
+{
+ gint undo_levels;
+
+ undo_levels = um->priv->max_undo_levels;
+
+ if (undo_levels < 1)
+ return;
+
+ if (um->priv->num_of_groups > undo_levels)
+ {
+ GtkSourceUndoAction *undo_action;
+
+ undo_action = action_list_last_data (um->priv->actions);
+
+ do
+ {
+ if (undo_action->order_in_group == 1)
+ --um->priv->num_of_groups;
+
+ if (undo_action->modified)
+ um->priv->modified_action = INVALID;
+
+ gtk_source_undo_action_free (undo_action);
+
+ action_list_delete_last (um->priv->actions);
+
+ undo_action = action_list_last_data (um->priv->actions);
+ g_return_if_fail (undo_action != NULL);
+
+ } while ((undo_action->order_in_group > 1) ||
+ (um->priv->num_of_groups > undo_levels));
+ }
+}
+
+/**
+ * gtk_source_undo_manager_default_merge_action:
+ * @um: a #GtkSourceUndoManagerDefault.
+ * @undo_action: a #GtkSourceUndoAction.
+ *
+ * This function tries to merge the undo action at the top of
+ * the stack with a new undo action. So when we undo for example
+ * typing, we can undo the whole word and not each letter by itself.
+ *
+ * Return Value: %TRUE is merge was sucessful, %FALSE otherwise.
+ **/
+static gboolean
+merge_action (GtkSourceUndoManagerDefault *um,
+ const GtkSourceUndoAction *undo_action)
+{
+ GtkSourceUndoAction *last_action;
+
+ g_return_val_if_fail (GTK_IS_SOURCE_UNDO_MANAGER_DEFAULT (um), FALSE);
+ g_return_val_if_fail (um->priv != NULL, FALSE);
+
+ if (um->priv->actions->len == 0)
+ return FALSE;
+
+ last_action = action_list_nth_data (um->priv->actions, 0);
+
+ if (!last_action->mergeable)
+ return FALSE;
+
+ if ((!undo_action->mergeable) ||
+ (undo_action->action_type != last_action->action_type))
+ {
+ last_action->mergeable = FALSE;
+ return FALSE;
+ }
+
+ if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
+ {
+ if ((last_action->action.delete.forward != undo_action->action.delete.forward) ||
+ ((last_action->action.delete.start != undo_action->action.delete.start) &&
+ (last_action->action.delete.start != undo_action->action.delete.end)))
+ {
+ last_action->mergeable = FALSE;
+ return FALSE;
+ }
+
+ if (last_action->action.delete.start == undo_action->action.delete.start)
+ {
+ gchar *str;
+
+#define L (last_action->action.delete.end - last_action->action.delete.start - 1)
+#define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i)))
+
+ /* Deleted with the delete key */
+ if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
+ (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
+ ((g_utf8_get_char_at (last_action->action.delete.text, L) == ' ') ||
+ (g_utf8_get_char_at (last_action->action.delete.text, L) == '\t')))
+ {
+ last_action->mergeable = FALSE;
+ return FALSE;
+ }
+
+ str = g_strdup_printf ("%s%s", last_action->action.delete.text,
+ undo_action->action.delete.text);
+
+ g_free (last_action->action.delete.text);
+ last_action->action.delete.end += (undo_action->action.delete.end -
+ undo_action->action.delete.start);
+ last_action->action.delete.text = str;
+ }
+ else
+ {
+ gchar *str;
+
+ /* Deleted with the backspace key */
+ if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
+ (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
+ ((g_utf8_get_char (last_action->action.delete.text) == ' ') ||
+ (g_utf8_get_char (last_action->action.delete.text) == '\t')))
+ {
+ last_action->mergeable = FALSE;
+ return FALSE;
+ }
+
+ str = g_strdup_printf ("%s%s", undo_action->action.delete.text,
+ last_action->action.delete.text);
+
+ g_free (last_action->action.delete.text);
+ last_action->action.delete.start = undo_action->action.delete.start;
+ last_action->action.delete.text = str;
+ }
+ }
+ else if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
+ {
+ gchar* str;
+
+#define I (last_action->action.insert.chars - 1)
+
+ if ((undo_action->action.insert.pos !=
+ (last_action->action.insert.pos + last_action->action.insert.chars)) ||
+ ((g_utf8_get_char (undo_action->action.insert.text) != ' ') &&
+ (g_utf8_get_char (undo_action->action.insert.text) != '\t') &&
+ ((g_utf8_get_char_at (last_action->action.insert.text, I) == ' ') ||
+ (g_utf8_get_char_at (last_action->action.insert.text, I) == '\t')))
+ )
+ {
+ last_action->mergeable = FALSE;
+ return FALSE;
+ }
+
+ str = g_strdup_printf ("%s%s", last_action->action.insert.text,
+ undo_action->action.insert.text);
+
+ g_free (last_action->action.insert.text);
+ last_action->action.insert.length += undo_action->action.insert.length;
+ last_action->action.insert.text = str;
+ last_action->action.insert.chars += undo_action->action.insert.chars;
+
+ }
+ else
+ /* Unknown action inside undo merge encountered */
+ g_return_val_if_reached (TRUE);
+
+ return TRUE;
+}
+
+static void
+modified_changed_handler (GtkTextBuffer *buffer,
+ GtkSourceUndoManagerDefault *manager)
+{
+ GtkSourceUndoAction *action;
+ gint idx;
+
+ if (manager->priv->actions->len == 0)
+ return;
+
+ idx = manager->priv->next_redo + 1;
+ action = action_list_nth_data (manager->priv->actions, idx);
+
+ if (gtk_text_buffer_get_modified (buffer) == FALSE)
+ {
+ if (action != NULL)
+ action->mergeable = FALSE;
+
+ if (manager->priv->modified_action != NULL)
+ {
+ if (manager->priv->modified_action != INVALID)
+ manager->priv->modified_action->modified = FALSE;
+
+ manager->priv->modified_action = NULL;
+ }
+
+ return;
+ }
+
+ if (action == NULL)
+ {
+ g_return_if_fail (manager->priv->running_not_undoable_actions > 0);
+
+ return;
+ }
+
+ if (manager->priv->modified_action != NULL)
+ {
+ g_message ("%s: oops", G_STRLOC);
+ return;
+ }
+
+ if (action->order_in_group > 1)
+ manager->priv->modified_undoing_group = TRUE;
+
+ while (action->order_in_group > 1)
+ {
+ action = action_list_nth_data (manager->priv->actions, ++idx);
+ g_return_if_fail (action != NULL);
+ }
+
+ action->modified = TRUE;
+ manager->priv->modified_action = action;
+}
+
+static void
+gtk_source_undo_manager_iface_init (GtkSourceUndoManagerIface *iface)
+{
+ iface->can_undo = gtk_source_undo_manager_can_undo_impl;
+ iface->can_redo = gtk_source_undo_manager_can_redo_impl;
+
+ iface->undo = gtk_source_undo_manager_undo_impl;
+ iface->redo = gtk_source_undo_manager_redo_impl;
+
+ iface->begin_not_undoable_action = gtk_source_undo_manager_begin_not_undoable_action_impl;
+ iface->end_not_undoable_action = gtk_source_undo_manager_end_not_undoable_action_impl;
+}
+
+void
+gtk_source_undo_manager_default_set_max_undo_levels (GtkSourceUndoManagerDefault *manager,
+ gint max_undo_levels)
+{
+ g_return_if_fail (GTK_IS_SOURCE_UNDO_MANAGER_DEFAULT (manager));
+
+ set_max_undo_levels (manager, max_undo_levels);
+ g_object_notify (G_OBJECT (manager), "max-undo-levels");
+}
diff --git a/gtksourceview/gtksourceundomanagerdefault.h b/gtksourceview/gtksourceundomanagerdefault.h
new file mode 100644
index 0000000..b714e66
--- /dev/null
+++ b/gtksourceview/gtksourceundomanagerdefault.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gtksourceundomanager_defaultdefault.h
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
+ * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
+ * Copyright (C) 2002, 2003 Paolo Maggi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GTK_SOURCE_UNDO_MANAGER_DEFAULT_H__
+#define __GTK_SOURCE_UNDO_MANAGER_DEFAULT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT (gtk_source_undo_manager_default_get_type ())
+#define GTK_SOURCE_UNDO_MANAGER_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT, GtkSourceUndoManagerDefault))
+#define GTK_SOURCE_UNDO_MANAGER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT, GtkSourceUndoManagerDefaultClass))
+#define GTK_IS_SOURCE_UNDO_MANAGER_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT))
+#define GTK_IS_SOURCE_UNDO_MANAGER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT))
+#define GTK_SOURCE_UNDO_MANAGER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT, GtkSourceUndoManagerDefaultClass))
+
+typedef struct _GtkSourceUndoManagerDefault GtkSourceUndoManagerDefault;
+typedef struct _GtkSourceUndoManagerDefaultClass GtkSourceUndoManagerDefaultClass;
+typedef struct _GtkSourceUndoManagerDefaultPrivate GtkSourceUndoManagerDefaultPrivate;
+
+struct _GtkSourceUndoManagerDefault
+{
+ GObject parent;
+
+ GtkSourceUndoManagerDefaultPrivate *priv;
+};
+
+struct _GtkSourceUndoManagerDefaultClass
+{
+ GObjectClass parent_class;
+};
+
+GType gtk_source_undo_manager_default_get_type (void) G_GNUC_CONST;
+
+void gtk_source_undo_manager_default_set_max_undo_levels (GtkSourceUndoManagerDefault *manager,
+ gint max_undo_levels);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_UNDO_MANAGER_DEFAULT_H__ */
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]