[gtksourceview] UndoManager: reorganize the order of the functions



commit e3a7c1fc4d36a6d538a40e3f97ff5f4b7d40ec70
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Thu Aug 21 22:50:34 2014 +0200

    UndoManager: reorganize the order of the functions
    
    So prototypes are not needed.

 gtksourceview/gtksourceundomanagerdefault.c |  952 +++++++++++++--------------
 1 files changed, 461 insertions(+), 491 deletions(-)
---
diff --git a/gtksourceview/gtksourceundomanagerdefault.c b/gtksourceview/gtksourceundomanagerdefault.c
index 358563a..aed9c98 100644
--- a/gtksourceview/gtksourceundomanagerdefault.c
+++ b/gtksourceview/gtksourceundomanagerdefault.c
@@ -135,36 +135,6 @@ struct _GtkSourceUndoManagerDefaultPrivate
        GtkSourceUndoAction *modified_action;
 };
 
-static void insert_text_cb            (GtkTextBuffer               *buffer,
-                                       GtkTextIter                 *pos,
-                                       const gchar                 *text,
-                                       gint                         length,
-                                       GtkSourceUndoManagerDefault *manager);
-
-static void delete_range_cb           (GtkTextBuffer               *buffer,
-                                       GtkTextIter                 *start,
-                                       GtkTextIter                 *end,
-                                       GtkSourceUndoManagerDefault *manager);
-
-static void begin_user_action_cb      (GtkTextBuffer               *buffer,
-                                       GtkSourceUndoManagerDefault *manager);
-
-static void modified_changed_cb       (GtkTextBuffer               *buffer,
-                                       GtkSourceUndoManagerDefault *manager);
-
-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,
@@ -173,229 +143,397 @@ G_DEFINE_TYPE_WITH_CODE (GtkSourceUndoManagerDefault, gtk_source_undo_manager_de
                                                 gtk_source_undo_manager_iface_init))
 
 static void
-gtk_source_undo_manager_default_finalize (GObject *object)
+gtk_source_undo_action_free (GtkSourceUndoAction *action)
 {
-       GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
+       if (action == NULL)
+       {
+               return;
+       }
 
-       free_action_list (manager);
-       g_ptr_array_free (manager->priv->actions, TRUE);
+       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_free (action);
+               g_return_if_reached ();
+       }
 
-       G_OBJECT_CLASS (gtk_source_undo_manager_default_parent_class)->finalize (object);
+       g_free (action);
 }
 
 static void
-clear_undo (GtkSourceUndoManagerDefault *manager)
+free_action_list (GtkSourceUndoManagerDefault *um)
 {
-       free_action_list (manager);
-
-       manager->priv->next_redo = -1;
+       gint i;
 
-       if (manager->priv->can_undo)
+       for (i = (gint)um->priv->actions->len - 1; i >= 0; i--)
        {
-               manager->priv->can_undo = FALSE;
-               gtk_source_undo_manager_can_undo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+               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 = NULL;
+
+               gtk_source_undo_action_free (action);
        }
 
-       if (manager->priv->can_redo)
+       /* Some arbitrary limit, to avoid wasting space */
+       if (um->priv->actions->len > 2048)
        {
-               manager->priv->can_redo = FALSE;
-               gtk_source_undo_manager_can_redo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+               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
-buffer_notify (GtkSourceUndoManagerDefault *manager,
-               gpointer                     where_the_object_was)
+static GtkSourceUndoAction *
+action_list_nth_data (GPtrArray *array,
+                      gint       n)
 {
-       manager->priv->buffer = NULL;
+       if (n < 0 || n >= (gint)array->len)
+               return NULL;
+       else
+               return array->pdata[array->len - 1 - n];
 }
 
 static void
-set_buffer (GtkSourceUndoManagerDefault *manager,
-            GtkTextBuffer               *buffer)
+action_list_prepend (GPtrArray           *array,
+                     GtkSourceUndoAction *action)
 {
-       g_assert (manager->priv->buffer == NULL);
+       g_ptr_array_add (array, action);
+}
 
-       if (buffer == NULL)
+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)
        {
-               return;
+               memmove (&array->pdata[0], &array->pdata[1], (array->len - 1)*sizeof (gpointer));
+               g_ptr_array_set_size (array, array->len - 1);
        }
+}
 
-       manager->priv->buffer = buffer;
+static void
+free_first_n_actions (GtkSourceUndoManagerDefault *um,
+                      gint                         n)
+{
+       gint i;
 
-       g_object_weak_ref (G_OBJECT (buffer),
-                          (GWeakNotify)buffer_notify,
-                          manager);
+       if (um->priv->actions->len == 0)
+               return;
 
-       g_signal_connect_object (buffer,
-                                "insert-text",
-                                G_CALLBACK (insert_text_cb),
-                                manager,
-                                0);
+       for (i = 0; i < n; i++)
+       {
+               GtkSourceUndoAction *action = um->priv->actions->pdata[um->priv->actions->len - 1];
 
-       g_signal_connect_object (buffer,
-                                "delete-range",
-                                G_CALLBACK (delete_range_cb),
-                                manager,
-                                0);
+               if (action->order_in_group == 1)
+                       --um->priv->num_of_groups;
 
-       g_signal_connect_object (buffer,
-                                "begin-user-action",
-                                G_CALLBACK (begin_user_action_cb),
-                                manager,
-                                0);
+               if (action->modified)
+                       um->priv->modified_action = NULL;
 
-       g_signal_connect_object (buffer,
-                                "modified-changed",
-                                G_CALLBACK (modified_changed_cb),
-                                manager,
-                                0);
+               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
-set_max_undo_levels (GtkSourceUndoManagerDefault *manager,
-                     gint                         max_undo_levels)
+check_list_size (GtkSourceUndoManagerDefault *um)
 {
-       gint old_levels;
+       gint undo_levels;
 
-       old_levels = manager->priv->max_undo_levels;
-       manager->priv->max_undo_levels = max_undo_levels;
+       undo_levels = um->priv->max_undo_levels;
 
-       if (max_undo_levels < 1)
+       if (undo_levels < 1)
                return;
 
-       if (old_levels > max_undo_levels)
+       if (um->priv->num_of_groups > undo_levels)
        {
-               /* strip redo actions first */
-               while (manager->priv->next_redo >= 0 &&
-                      (manager->priv->num_of_groups > max_undo_levels))
+               GtkSourceUndoAction *undo_action;
+
+               undo_action = action_list_last_data (um->priv->actions);
+
+               do
                {
-                       free_first_n_actions (manager, 1);
-                       manager->priv->next_redo--;
-               }
+                       if (undo_action->order_in_group == 1)
+                               --um->priv->num_of_groups;
 
-               /* now remove undo actions if necessary */
-               check_list_size (manager);
+                       if (undo_action->modified)
+                               um->priv->modified_action = NULL;
 
-               /* emit "can_undo" and/or "can_redo" if appropiate */
-               if (manager->priv->next_redo < 0 && manager->priv->can_redo)
+                       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));
+       }
+}
+
+/*
+ * 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_SOURCE_IS_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)))
                {
-                       manager->priv->can_redo = FALSE;
-                       gtk_source_undo_manager_can_redo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+                       last_action->mergeable = FALSE;
+                       return FALSE;
                }
 
-               if (manager->priv->can_undo &&
-                   manager->priv->next_redo >= (gint)manager->priv->actions->len - 1)
+               if (last_action->action.delete.start == undo_action->action.delete.start)
                {
-                       manager->priv->can_undo = FALSE;
-                       gtk_source_undo_manager_can_undo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+                       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;
 
-static void
-gtk_source_undo_manager_default_dispose (GObject *object)
-{
-       GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
+                       /* 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;
+                       }
 
-       if (manager->priv->buffer != NULL)
+                       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)
        {
-               clear_undo (manager);
+               gchar* str;
 
-               g_object_weak_unref (G_OBJECT (manager->priv->buffer),
-                                    (GWeakNotify)buffer_notify,
-                                    manager);
+#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;
 
-               manager->priv->buffer = NULL;
        }
+       else
+               /* Unknown action inside undo merge encountered */
+               g_return_val_if_reached (TRUE);
 
-       G_OBJECT_CLASS (gtk_source_undo_manager_default_parent_class)->dispose (object);
+       return TRUE;
 }
 
 static void
-gtk_source_undo_manager_default_set_property (GObject      *object,
-                                              guint         prop_id,
-                                              const GValue *value,
-                                              GParamSpec   *pspec)
+add_action (GtkSourceUndoManagerDefault *um,
+            const GtkSourceUndoAction   *undo_action)
 {
-       GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
+       GtkSourceUndoAction* action;
 
-       switch (prop_id)
+       if (um->priv->next_redo >= 0)
        {
-               case PROP_BUFFER:
-                       set_buffer (manager, g_value_get_object (value));
-                       break;
+               free_first_n_actions (um, um->priv->next_redo + 1);
+       }
 
-               case PROP_MAX_UNDO_LEVELS:
-                       gtk_source_undo_manager_default_set_max_undo_levels (manager, g_value_get_int 
(value));
-                       break;
+       um->priv->next_redo = -1;
 
-               default:
-                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-                       break;
+       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
-gtk_source_undo_manager_default_get_property (GObject    *object,
-                                              guint       prop_id,
-                                              GValue     *value,
-                                              GParamSpec *pspec)
+clear_undo (GtkSourceUndoManagerDefault *manager)
 {
-       GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
+       free_action_list (manager);
 
-       switch (prop_id)
-       {
-               case PROP_BUFFER:
-                       g_value_set_object (value, manager->priv->buffer);
-                       break;
+       manager->priv->next_redo = -1;
 
-               case PROP_MAX_UNDO_LEVELS:
-                       g_value_set_int (value, manager->priv->max_undo_levels);
-                       break;
+       if (manager->priv->can_undo)
+       {
+               manager->priv->can_undo = FALSE;
+               gtk_source_undo_manager_can_undo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
+       }
 
-               default:
-                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-                       break;
+       if (manager->priv->can_redo)
+       {
+               manager->priv->can_redo = FALSE;
+               gtk_source_undo_manager_can_redo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
        }
 }
 
 static void
-gtk_source_undo_manager_default_class_init (GtkSourceUndoManagerDefaultClass *klass)
+set_max_undo_levels (GtkSourceUndoManagerDefault *manager,
+                     gint                         max_undo_levels)
 {
-       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       gint old_levels;
 
-       object_class->finalize = gtk_source_undo_manager_default_finalize;
-       object_class->dispose = gtk_source_undo_manager_default_dispose;
+       old_levels = manager->priv->max_undo_levels;
+       manager->priv->max_undo_levels = max_undo_levels;
 
-       object_class->set_property = gtk_source_undo_manager_default_set_property;
-       object_class->get_property = gtk_source_undo_manager_default_get_property;
+       if (max_undo_levels < 1)
+               return;
 
-       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));
+       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--;
+               }
 
-       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));
-}
+               /* now remove undo actions if necessary */
+               check_list_size (manager);
 
-static void
-gtk_source_undo_manager_default_init (GtkSourceUndoManagerDefault *manager)
-{
-       manager->priv = gtk_source_undo_manager_default_get_instance_private (manager);
-       manager->priv->actions = g_ptr_array_new ();
-       manager->priv->next_redo = -1;
+               /* 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
@@ -517,42 +655,6 @@ get_chars (GtkTextBuffer *buffer,
        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
 selection_bounds_offsets (GtkTextBuffer *buffer,
                           gint          *insert_offset,
@@ -805,61 +907,6 @@ gtk_source_undo_manager_redo_impl (GtkSourceUndoManager *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_free (action);
-               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 = NULL;
-
-               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_cb (GtkTextBuffer               *buffer,
                GtkTextIter                 *pos,
                const gchar                 *text,
@@ -950,298 +997,221 @@ begin_user_action_cb (GtkTextBuffer               *buffer,
 }
 
 static void
-add_action (GtkSourceUndoManagerDefault *um,
-            const GtkSourceUndoAction   *undo_action)
+modified_changed_cb (GtkTextBuffer               *buffer,
+                    GtkSourceUndoManagerDefault *manager)
 {
-       GtkSourceUndoAction* action;
+       GtkSourceUndoAction *action;
+       gint idx;
 
-       if (um->priv->next_redo >= 0)
-       {
-               free_first_n_actions (um, um->priv->next_redo + 1);
-       }
+       if (manager->priv->actions->len == 0)
+               return;
 
-       um->priv->next_redo = -1;
+       idx = manager->priv->next_redo + 1;
+       action = action_list_nth_data (manager->priv->actions, idx);
 
-       if (!merge_action (um, undo_action))
+       if (gtk_text_buffer_get_modified (buffer) == FALSE)
        {
-               action = g_new (GtkSourceUndoAction, 1);
-               *action = *undo_action;
+               if (action != NULL)
+                       action->mergeable = FALSE;
 
-               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
+               if (manager->priv->modified_action != NULL)
                {
-                       g_free (action);
-                       g_return_if_reached ();
+                       manager->priv->modified_action->modified = FALSE;
+                       manager->priv->modified_action = NULL;
                }
 
-               ++um->priv->actions_in_current_group;
-               action->order_in_group = um->priv->actions_in_current_group;
+               return;
+       }
 
-               if (action->order_in_group == 1)
-                       ++um->priv->num_of_groups;
+       if (action == NULL)
+       {
+               g_return_if_fail (manager->priv->running_not_undoable_actions > 0);
 
-               action_list_prepend (um->priv->actions, action);
+               return;
        }
 
-       check_list_size (um);
-
-       if (!um->priv->can_undo)
+       if (manager->priv->modified_action != NULL)
        {
-               um->priv->can_undo = TRUE;
-               gtk_source_undo_manager_can_undo_changed (GTK_SOURCE_UNDO_MANAGER (um));
+               g_message ("%s: oops", G_STRLOC);
+               return;
        }
 
-       if (um->priv->can_redo)
+       if (action->order_in_group > 1)
+               manager->priv->modified_undoing_group  = TRUE;
+
+       while (action->order_in_group > 1)
        {
-               um->priv->can_redo = FALSE;
-               gtk_source_undo_manager_can_redo_changed (GTK_SOURCE_UNDO_MANAGER (um));
+               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
-free_first_n_actions (GtkSourceUndoManagerDefault *um,
-                      gint                         n)
+buffer_notify (GtkSourceUndoManagerDefault *manager,
+               gpointer                     where_the_object_was)
 {
-       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 = NULL;
-
-               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;
-       }
+       manager->priv->buffer = NULL;
 }
 
 static void
-check_list_size (GtkSourceUndoManagerDefault *um)
+set_buffer (GtkSourceUndoManagerDefault *manager,
+            GtkTextBuffer               *buffer)
 {
-       gint undo_levels;
-
-       undo_levels = um->priv->max_undo_levels;
-
-       if (undo_levels < 1)
-               return;
+       g_assert (manager->priv->buffer == NULL);
 
-       if (um->priv->num_of_groups > undo_levels)
+       if (buffer == NULL)
        {
-               GtkSourceUndoAction *undo_action;
-
-               undo_action = action_list_last_data (um->priv->actions);
+               return;
+       }
 
-               do
-               {
-                       if (undo_action->order_in_group == 1)
-                               --um->priv->num_of_groups;
+       manager->priv->buffer = buffer;
 
-                       if (undo_action->modified)
-                               um->priv->modified_action = NULL;
+       g_object_weak_ref (G_OBJECT (buffer),
+                          (GWeakNotify)buffer_notify,
+                          manager);
 
-                       gtk_source_undo_action_free (undo_action);
+       g_signal_connect_object (buffer,
+                                "insert-text",
+                                G_CALLBACK (insert_text_cb),
+                                manager,
+                                0);
 
-                       action_list_delete_last (um->priv->actions);
+       g_signal_connect_object (buffer,
+                                "delete-range",
+                                G_CALLBACK (delete_range_cb),
+                                manager,
+                                0);
 
-                       undo_action = action_list_last_data (um->priv->actions);
-                       g_return_if_fail (undo_action != NULL);
+       g_signal_connect_object (buffer,
+                                "begin-user-action",
+                                G_CALLBACK (begin_user_action_cb),
+                                manager,
+                                0);
 
-               } while ((undo_action->order_in_group > 1) ||
-                        (um->priv->num_of_groups > undo_levels));
-       }
+       g_signal_connect_object (buffer,
+                                "modified-changed",
+                                G_CALLBACK (modified_changed_cb),
+                                manager,
+                                0);
 }
 
-/**
- * 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)
+static void
+gtk_source_undo_manager_default_dispose (GObject *object)
 {
-       GtkSourceUndoAction *last_action;
-
-       g_return_val_if_fail (GTK_SOURCE_IS_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;
-       }
+       GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
 
-       if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
+       if (manager->priv->buffer != NULL)
        {
-               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;
-               }
+               clear_undo (manager);
 
-               if (last_action->action.delete.start == undo_action->action.delete.start)
-               {
-                       gchar *str;
+               g_object_weak_unref (G_OBJECT (manager->priv->buffer),
+                                    (GWeakNotify)buffer_notify,
+                                    manager);
 
-#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)))
+               manager->priv->buffer = NULL;
+       }
 
-                       /* 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;
-                       }
+       G_OBJECT_CLASS (gtk_source_undo_manager_default_parent_class)->dispose (object);
+}
 
-                       str = g_strdup_printf ("%s%s", last_action->action.delete.text,
-                               undo_action->action.delete.text);
+static void
+gtk_source_undo_manager_default_finalize (GObject *object)
+{
+       GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
 
-                       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;
+       free_action_list (manager);
+       g_ptr_array_free (manager->priv->actions, TRUE);
 
-                       /* 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;
-                       }
+       G_OBJECT_CLASS (gtk_source_undo_manager_default_parent_class)->finalize (object);
+}
 
-                       str = g_strdup_printf ("%s%s", undo_action->action.delete.text,
-                               last_action->action.delete.text);
+static void
+gtk_source_undo_manager_default_set_property (GObject      *object,
+                                              guint         prop_id,
+                                              const GValue *value,
+                                              GParamSpec   *pspec)
+{
+       GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
 
-                       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)
+       switch (prop_id)
        {
-               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);
+               case PROP_BUFFER:
+                       set_buffer (manager, g_value_get_object (value));
+                       break;
 
-               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;
+               case PROP_MAX_UNDO_LEVELS:
+                       gtk_source_undo_manager_default_set_max_undo_levels (manager, g_value_get_int 
(value));
+                       break;
 
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
        }
-       else
-               /* Unknown action inside undo merge encountered */
-               g_return_val_if_reached (TRUE);
-
-       return TRUE;
 }
 
 static void
-modified_changed_cb (GtkTextBuffer               *buffer,
-                    GtkSourceUndoManagerDefault *manager)
+gtk_source_undo_manager_default_get_property (GObject    *object,
+                                              guint       prop_id,
+                                              GValue     *value,
+                                              GParamSpec *pspec)
 {
-       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);
+       GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
 
-       if (gtk_text_buffer_get_modified (buffer) == FALSE)
+       switch (prop_id)
        {
-               if (action != NULL)
-                       action->mergeable = FALSE;
+               case PROP_BUFFER:
+                       g_value_set_object (value, manager->priv->buffer);
+                       break;
 
-               if (manager->priv->modified_action != NULL)
-               {
-                       manager->priv->modified_action->modified = FALSE;
-                       manager->priv->modified_action = NULL;
-               }
+               case PROP_MAX_UNDO_LEVELS:
+                       g_value_set_int (value, manager->priv->max_undo_levels);
+                       break;
 
-               return;
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
        }
+}
 
-       if (action == NULL)
-       {
-               g_return_if_fail (manager->priv->running_not_undoable_actions > 0);
+static void
+gtk_source_undo_manager_default_class_init (GtkSourceUndoManagerDefaultClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-               return;
-       }
+       object_class->dispose = gtk_source_undo_manager_default_dispose;
+       object_class->finalize = gtk_source_undo_manager_default_finalize;
 
-       if (manager->priv->modified_action != NULL)
-       {
-               g_message ("%s: oops", G_STRLOC);
-               return;
-       }
+       object_class->set_property = gtk_source_undo_manager_default_set_property;
+       object_class->get_property = gtk_source_undo_manager_default_get_property;
 
-       if (action->order_in_group > 1)
-               manager->priv->modified_undoing_group  = TRUE;
+       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));
 
-       while (action->order_in_group > 1)
-       {
-               action = action_list_nth_data (manager->priv->actions, ++idx);
-               g_return_if_fail (action != NULL);
-       }
+       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));
+}
 
-       action->modified = TRUE;
-       manager->priv->modified_action = action;
+static void
+gtk_source_undo_manager_default_init (GtkSourceUndoManagerDefault *manager)
+{
+       manager->priv = gtk_source_undo_manager_default_get_instance_private (manager);
+       manager->priv->actions = g_ptr_array_new ();
+       manager->priv->next_redo = -1;
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]