[gtk/wip/otte/undo: 6/17] entry: Rework undo
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/undo: 6/17] entry: Rework undo
- Date: Mon, 18 Feb 2019 17:12:54 +0000 (UTC)
commit 8e8ee8142ee67597d35af0dd2568ed54a0261cb8
Author: Benjamin Otte <otte redhat com>
Date: Sun Aug 16 03:09:09 2015 +0200
entry: Rework undo
Instead of storing the actions that were performed, store the before
and after state of the entry.
This also allows for a very easy way to do merges.
gtk/gtkentry.c | 64 ++++++++------
gtk/gtkentryundocommand.c | 186 ++++++++++++++++++++++++++++-----------
gtk/gtkentryundocommandprivate.h | 17 +++-
3 files changed, 185 insertions(+), 82 deletions(-)
---
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index 25fda154c9..438d0e07e5 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -5371,6 +5371,17 @@ gtk_entry_remove_password_hint (gpointer data)
return FALSE;
}
+static void
+gtk_entry_record_undo_command (GtkEntry *entry,
+ const GtkEntrySnapshot *before)
+{
+ GtkUndoCommand *command;
+
+ command = gtk_entry_undo_command_new (entry, before);
+ gtk_undo_stack_push (entry->priv->undo_stack, command);
+ g_object_unref (command);
+}
+
/* Default signal handlers
*/
static void
@@ -5379,6 +5390,9 @@ gtk_entry_real_insert_text (GtkEditable *editable,
gint new_text_length,
gint *position)
{
+ GtkEntry *entry = GTK_ENTRY (editable);
+ GtkEntryPrivate *priv = entry->priv;
+ GtkEntrySnapshot snapshot = { NULL, };
guint n_inserted;
gint n_chars;
@@ -5389,11 +5403,20 @@ gtk_entry_real_insert_text (GtkEditable *editable,
* following signal handlers: buffer_inserted_text(), buffer_notify_display_text(),
* buffer_notify_text(), buffer_notify_length()
*/
- begin_change (GTK_ENTRY (editable));
+ if (priv->undo_mode == GTK_ENTRY_UNDO_RECORD)
+ gtk_entry_snapshot_init_from_entry (&snapshot, entry);
- n_inserted = gtk_entry_buffer_insert_text (get_buffer (GTK_ENTRY (editable)), *position, new_text,
n_chars);
+ begin_change (entry);
- end_change (GTK_ENTRY (editable));
+ n_inserted = gtk_entry_buffer_insert_text (get_buffer (entry), *position, new_text, n_chars);
+
+ end_change (entry);
+
+ if (priv->undo_mode == GTK_ENTRY_UNDO_RECORD)
+ {
+ gtk_entry_record_undo_command (entry, &snapshot);
+ gtk_entry_snapshot_clear (&snapshot);
+ }
if (n_inserted != n_chars)
gtk_widget_error_bell (GTK_WIDGET (editable));
@@ -5401,19 +5424,6 @@ gtk_entry_real_insert_text (GtkEditable *editable,
*position += n_inserted;
}
-static void
-gtk_entry_record_undo_command (GtkEntry *entry,
- int start_pos,
- const char *deleted_text,
- const char *inserted_text)
-{
- GtkUndoCommand *command;
-
- command = gtk_entry_undo_command_new (entry, start_pos, deleted_text, inserted_text);
- gtk_undo_stack_push (entry->priv->undo_stack, command);
- g_object_unref (command);
-}
-
static void
gtk_entry_real_delete_text (GtkEditable *editable,
gint start_pos,
@@ -5421,6 +5431,7 @@ gtk_entry_real_delete_text (GtkEditable *editable,
{
GtkEntry *entry = GTK_ENTRY (editable);
GtkEntryPrivate *priv = entry->priv;
+ GtkEntrySnapshot snapshot = { NULL, };
/*
* The actual deletion from the buffer. This will end up firing the
@@ -5428,23 +5439,20 @@ gtk_entry_real_delete_text (GtkEditable *editable,
* buffer_notify_text(), buffer_notify_length()
*/
- begin_change (entry);
-
if (priv->undo_mode == GTK_ENTRY_UNDO_RECORD)
- {
- char *deleted_text = gtk_entry_get_chars (GTK_EDITABLE (entry), start_pos, end_pos);
-
- gtk_entry_record_undo_command (entry,
- start_pos,
- deleted_text,
- NULL);
+ gtk_entry_snapshot_init_from_entry (&snapshot, entry);
- g_free (deleted_text);
- }
+ begin_change (entry);
gtk_entry_buffer_delete_text (get_buffer (entry), start_pos, end_pos - start_pos);
end_change (entry);
+
+ if (priv->undo_mode == GTK_ENTRY_UNDO_RECORD)
+ {
+ gtk_entry_record_undo_command (entry, &snapshot);
+ gtk_entry_snapshot_clear (&snapshot);
+ }
}
/* GtkEntryBuffer signal handlers
@@ -5473,8 +5481,6 @@ buffer_inserted_text (GtkEntryBuffer *buffer,
if (priv->undo_mode == GTK_ENTRY_UNDO_RESET)
gtk_undo_stack_clear (priv->undo_stack);
- else if (priv->undo_mode == GTK_ENTRY_UNDO_RECORD)
- gtk_entry_record_undo_command (entry, position, NULL, chars);
/* Calculate the password hint if it needs to be displayed. */
if (n_chars == 1 && !priv->visible)
diff --git a/gtk/gtkentryundocommand.c b/gtk/gtkentryundocommand.c
index e9fa703aca..6a902ea38d 100644
--- a/gtk/gtkentryundocommand.c
+++ b/gtk/gtkentryundocommand.c
@@ -27,21 +27,50 @@
#include "gtkentryprivate.h"
typedef struct _GtkEntryUndoCommandPrivate GtkEntryUndoCommandPrivate;
+
struct _GtkEntryUndoCommandPrivate {
GtkEntry *entry; /* entry we're operating on or NULL if entry was deleted */
- gsize char_offset; /* offset in characters into entry's text */
- char *text_deleted; /* text that will be deleted if we *redo* this command */
- char *text_entered; /* text that will be entered if we *redo* this command */
+ GtkEntrySnapshot before; /* what we undo to */
+ GtkEntrySnapshot after; /* what we redo to */
};
G_DEFINE_TYPE_WITH_CODE (GtkEntryUndoCommand, gtk_entry_undo_command, GTK_TYPE_UNDO_COMMAND,
G_ADD_PRIVATE (GtkEntryUndoCommand))
+void
+gtk_entry_snapshot_init_from_entry (GtkEntrySnapshot *snapshot,
+ GtkEntry *entry)
+{
+ gint start, end;
+
+ snapshot->text = g_strdup (gtk_entry_get_text (entry));
+ gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end);
+ snapshot->cursor = gtk_editable_get_position (GTK_EDITABLE (entry));
+ if (start == snapshot->cursor)
+ snapshot->selection_start = end;
+ else
+ snapshot->selection_start = start;
+}
+
+void
+gtk_entry_snapshot_clear (GtkEntrySnapshot *snapshot)
+{
+ g_free (snapshot->text);
+ snapshot->text = NULL;
+}
+
+static void
+gtk_entry_snapshot_copy (GtkEntrySnapshot *target,
+ const GtkEntrySnapshot *source)
+{
+ target->text = g_strdup (source->text);
+ target->cursor = source->cursor;
+ target->selection_start = source->selection_start;
+}
+
static gboolean
-gtk_entry_undo_command_run (GtkEntry *entry,
- int position,
- int chars_to_delete,
- const char *text_to_insert)
+gtk_entry_undo_command_run (GtkEntry *entry,
+ const GtkEntrySnapshot *snapshot)
{
GtkEntryUndoMode old_mode;
@@ -50,18 +79,8 @@ gtk_entry_undo_command_run (GtkEntry *entry,
old_mode = gtk_entry_set_undo_mode (entry, GTK_ENTRY_UNDO_REPLAY);
- if (chars_to_delete)
- gtk_editable_delete_text (GTK_EDITABLE (entry),
- position,
- position + chars_to_delete);
-
- if (text_to_insert)
- gtk_editable_insert_text (GTK_EDITABLE (entry),
- text_to_insert,
- -1,
- &position);
-
- gtk_editable_set_position (GTK_EDITABLE (entry), position);
+ gtk_entry_set_text (entry, snapshot->text);
+ gtk_editable_select_region (GTK_EDITABLE (entry), snapshot->cursor, snapshot->selection_start);
gtk_entry_set_undo_mode (entry, old_mode);
@@ -73,10 +92,7 @@ gtk_entry_undo_command_undo (GtkUndoCommand *command)
{
GtkEntryUndoCommandPrivate *priv = gtk_entry_undo_command_get_instance_private (GTK_ENTRY_UNDO_COMMAND
(command));
- return gtk_entry_undo_command_run (priv->entry,
- priv->char_offset,
- priv->text_entered ? g_utf8_strlen (priv->text_entered, -1) : 0,
- priv->text_deleted);
+ return gtk_entry_undo_command_run (priv->entry, &priv->before);
}
gboolean
@@ -84,30 +100,105 @@ gtk_entry_undo_command_redo (GtkUndoCommand *command)
{
GtkEntryUndoCommandPrivate *priv = gtk_entry_undo_command_get_instance_private (GTK_ENTRY_UNDO_COMMAND
(command));
- return gtk_entry_undo_command_run (priv->entry,
- priv->char_offset,
- priv->text_deleted ? g_utf8_strlen (priv->text_deleted, -1) : 0,
- priv->text_entered);
+ return gtk_entry_undo_command_run (priv->entry, &priv->after);
+}
+
+static GtkUndoCommand *
+gtk_entry_undo_command_new_from_snapshots (GtkEntry *entry,
+ const GtkEntrySnapshot *before,
+ const GtkEntrySnapshot *after)
+{
+ GtkEntryUndoCommand *command;
+ GtkEntryUndoCommandPrivate *priv;
+
+ command = g_object_new (GTK_TYPE_ENTRY_UNDO_COMMAND, NULL);
+ priv = gtk_entry_undo_command_get_instance_private (command);
+
+ priv->entry = entry;
+ gtk_entry_snapshot_copy (&priv->before, before);
+ gtk_entry_snapshot_copy (&priv->after, after);
+
+ return GTK_UNDO_COMMAND (command);
}
GtkUndoCommand *
gtk_entry_undo_command_merge (GtkUndoCommand *command,
GtkUndoCommand *followup)
{
- return NULL;
+ GtkEntryUndoCommandPrivate *command_priv = gtk_entry_undo_command_get_instance_private
(GTK_ENTRY_UNDO_COMMAND (command));
+ GtkEntryUndoCommandPrivate *followup_priv = gtk_entry_undo_command_get_instance_private
(GTK_ENTRY_UNDO_COMMAND (followup));
+
+ if (!GTK_IS_ENTRY_UNDO_COMMAND (followup))
+ return NULL;
+
+ if (command_priv->entry != followup_priv->entry)
+ return NULL;
+
+ if (!g_str_equal (command_priv->after.text, followup_priv->before.text))
+ return NULL;
+
+ /* We don't insist on cursor positions being equal here, someone
+ * might ie. move the cursor to correct a typo
+ */
+ return gtk_entry_undo_command_new_from_snapshots (command_priv->entry, &command_priv->before,
&followup_priv->after);
+}
+
+static guint
+get_prefix_len (const char *str1,
+ const char *str2)
+{
+ guint i;
+
+ for (i = 0; str1[i] == str2[i] && str1[i] != 0; i++)
+ {
+ /* nothing to do here */
+ }
+
+ return i;
+}
+
+static guint
+get_suffix_len (const char *str1,
+ guint len1,
+ const char *str2,
+ guint len2)
+{
+ const char *cur1, *cur2;
+ guint i, max_len;
+
+ cur1 = str1 + len1 - 1;
+ cur2 = str2 + len2 - 1;
+ max_len = MIN (len1, len2);
+
+ for (i = 0; *cur1 == *cur2 && i < max_len; i++)
+ {
+ cur1--;
+ cur2--;
+ }
+
+ return i;
}
char *
gtk_entry_undo_command_describe (GtkUndoCommand *command)
{
GtkEntryUndoCommandPrivate *priv = gtk_entry_undo_command_get_instance_private (GTK_ENTRY_UNDO_COMMAND
(command));
-
- if (priv->text_entered)
- return g_strdup_printf (_("Entered `%s'"), priv->text_entered);
- else if (priv->text_deleted)
- return g_strdup_printf (_("Deleted `%s'"), priv->text_deleted);
+ guint before_len, after_len, prefix_len, suffix_len;
+
+ before_len = strlen (priv->before.text);
+ after_len = strlen (priv->after.text);
+ prefix_len = get_prefix_len (priv->before.text, priv->after.text);
+ suffix_len = get_suffix_len (priv->before.text, before_len,
+ priv->after.text, after_len);
+
+ if (before_len == after_len && before_len == prefix_len)
+ return g_strdup (_("No changes")); /* huh? */
+ else if (prefix_len + suffix_len == before_len)
+ return g_strdup_printf (_("Entered `%*s'"), after_len - prefix_len - suffix_len, priv->after.text +
prefix_len);
+ else if (prefix_len + suffix_len == after_len)
+ return g_strdup_printf (_("Deleted `%*s'"), before_len - prefix_len - suffix_len, priv->before.text +
prefix_len);
else
- return g_strdup (_("Did nothing"));
+ return g_strdup (_("Text changed"));
}
static void
@@ -115,8 +206,8 @@ gtk_entry_undo_command_finalize (GObject *object)
{
GtkEntryUndoCommandPrivate *priv = gtk_entry_undo_command_get_instance_private (GTK_ENTRY_UNDO_COMMAND
(object));
- g_free (priv->text_deleted);
- g_free (priv->text_entered);
+ gtk_entry_snapshot_clear (&priv->before);
+ gtk_entry_snapshot_clear (&priv->after);
G_OBJECT_CLASS (gtk_entry_undo_command_parent_class)->finalize (object);
}
@@ -141,24 +232,21 @@ gtk_entry_undo_command_init (GtkEntryUndoCommand *command)
}
GtkUndoCommand *
-gtk_entry_undo_command_new (GtkEntry *entry,
- int position,
- const char *text_deleted,
- const char *text_entered)
+gtk_entry_undo_command_new (GtkEntry *entry,
+ const GtkEntrySnapshot *before)
{
- GtkEntryUndoCommand *result;
- GtkEntryUndoCommandPrivate *priv;
+ GtkEntrySnapshot after;
+ GtkUndoCommand *result;
g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+ g_return_val_if_fail (before != NULL, NULL);
- result = g_object_new (GTK_TYPE_ENTRY_UNDO_COMMAND, NULL);
+ gtk_entry_snapshot_init_from_entry (&after, entry);
- priv = gtk_entry_undo_command_get_instance_private (result);
- priv->entry = entry;
- priv->char_offset = position;
- priv->text_deleted = g_strdup (text_deleted);
- priv->text_entered = g_strdup (text_entered);
+ result = gtk_entry_undo_command_new_from_snapshots (entry, before, &after);
+
+ gtk_entry_snapshot_clear (&after);
- return GTK_UNDO_COMMAND (result);
+ return result;
}
diff --git a/gtk/gtkentryundocommandprivate.h b/gtk/gtkentryundocommandprivate.h
index 23b69a648e..267c35284b 100644
--- a/gtk/gtkentryundocommandprivate.h
+++ b/gtk/gtkentryundocommandprivate.h
@@ -34,6 +34,13 @@ G_BEGIN_DECLS
typedef struct _GtkEntryUndoCommand GtkEntryUndoCommand;
typedef struct _GtkEntryUndoCommandClass GtkEntryUndoCommandClass;
+typedef struct _GtkEntrySnapshot GtkEntrySnapshot;
+
+struct _GtkEntrySnapshot {
+ char *text; /* text of the whole entry */
+ guint cursor; /* cursor position */
+ guint selection_start; /* selection start. Equal to cursor if no selection */
+};
struct _GtkEntryUndoCommand
{
@@ -47,10 +54,12 @@ struct _GtkEntryUndoCommandClass
GType gtk_entry_undo_command_get_type (void) G_GNUC_CONST;
-GtkUndoCommand * gtk_entry_undo_command_new (GtkEntry *entry,
- int position,
- const char *text_deleted,
- const char *text_entered);
+GtkUndoCommand * gtk_entry_undo_command_new (GtkEntry *entry,
+ const GtkEntrySnapshot *before);
+
+void gtk_entry_snapshot_init_from_entry (GtkEntrySnapshot *snapshot,
+ GtkEntry *entry);
+void gtk_entry_snapshot_clear (GtkEntrySnapshot *snapshot);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]