[gtk/wip/otte/undo: 2/17] entry: Add undo stack support
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/undo: 2/17] entry: Add undo stack support
- Date: Mon, 18 Feb 2019 17:12:34 +0000 (UTC)
commit fd9c18f022a15f2b53b984234e7ae4ab19325be0
Author: Benjamin Otte <otte redhat com>
Date: Tue Aug 4 03:02:47 2015 +0200
entry: Add undo stack support
So far, undo commands are just recorded, no support for actually undoing
them exists.
gtk/Makefile.am | 2 +
gtk/gtkentry.c | 90 +++++++++++++++++++--
gtk/gtkentryprivate.h | 9 +++
gtk/gtkentryundocommand.c | 164 +++++++++++++++++++++++++++++++++++++++
gtk/gtkentryundocommandprivate.h | 57 ++++++++++++++
5 files changed, 317 insertions(+), 5 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index f75dfc4736..6d404e4d58 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -427,6 +427,7 @@ gtk_private_h_sources = \
gtkdialogprivate.h \
gtkdndprivate.h \
gtkentryprivate.h \
+ gtkentryundocommandprivate.h \
gtkeventcontrollerprivate.h \
gtkfilechooserembed.h \
gtkfilechooserentry.h \
@@ -676,6 +677,7 @@ gtk_base_c_sources = \
gtkentry.c \
gtkentrybuffer.c \
gtkentrycompletion.c \
+ gtkentryundocommand.c \
gtkeventbox.c \
gtkeventcontroller.c \
gtkexpander.c \
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index a7d04fbb82..8c43b8ae07 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -38,6 +38,7 @@
#include "gtkdnd.h"
#include "gtkentry.h"
#include "gtkentrybuffer.h"
+#include "gtkentryundocommandprivate.h"
#include "gtkiconhelperprivate.h"
#include "gtkimcontextsimple.h"
#include "gtkimmulticontext.h"
@@ -65,6 +66,7 @@
#include "gtkwidgetprivate.h"
#include "gtkstylecontextprivate.h"
#include "gtktexthandleprivate.h"
+#include "gtkundostackprivate.h"
#include "gtkpopover.h"
#include "gtktoolbar.h"
#include "gtkmagnifierprivate.h"
@@ -150,7 +152,8 @@ struct _GtkEntryPrivate
PangoAttrList *attrs;
PangoTabArray *tabs;
- gchar *im_module;
+ gchar *im_module;
+ GtkUndoStack *undo_stack;
gdouble progress_fraction;
gdouble progress_pulse_fraction;
@@ -210,6 +213,7 @@ struct _GtkEntryPrivate
guint overwrite_mode : 1;
guint visible : 1;
+ GtkEntryUndoMode undo_mode : 2;
guint activates_default : 1;
guint cache_includes_preedit : 1;
guint caps_lock_warning : 1;
@@ -2737,6 +2741,8 @@ gtk_entry_init (GtkEntry *entry)
g_signal_connect (priv->im_context, "delete-surrounding",
G_CALLBACK (gtk_entry_delete_surrounding_cb), entry);
+ priv->undo_stack = gtk_undo_stack_new ();
+
context = gtk_widget_get_style_context (GTK_WIDGET (entry));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_ENTRY);
@@ -3027,6 +3033,8 @@ gtk_entry_finalize (GObject *object)
g_object_unref (priv->im_context);
+ g_object_unref (priv->undo_stack);
+
if (priv->blink_timeout)
g_source_remove (priv->blink_timeout);
@@ -5359,22 +5367,50 @@ 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,
gint end_pos)
{
+ GtkEntry *entry = GTK_ENTRY (editable);
+ GtkEntryPrivate *priv = entry->priv;
+
/*
* The actual deletion from the buffer. This will end up firing the
* following signal handlers: buffer_deleted_text(), buffer_notify_display_text(),
* buffer_notify_text(), buffer_notify_length()
*/
- begin_change (GTK_ENTRY (editable));
+ begin_change (entry);
- gtk_entry_buffer_delete_text (get_buffer (GTK_ENTRY (editable)), start_pos, end_pos - start_pos);
+ if (priv->undo_mode == GTK_ENTRY_UNDO_RECORD)
+ {
+ char *deleted_text = gtk_entry_get_chars (GTK_EDITABLE (entry), start_pos, end_pos);
- end_change (GTK_ENTRY (editable));
+ gtk_entry_record_undo_command (entry,
+ start_pos,
+ deleted_text,
+ NULL);
+
+ g_free (deleted_text);
+ }
+
+ gtk_entry_buffer_delete_text (get_buffer (entry), start_pos, end_pos - start_pos);
+
+ end_change (entry);
}
/* GtkEntryBuffer signal handlers
@@ -5401,6 +5437,11 @@ buffer_inserted_text (GtkEntryBuffer *buffer,
gtk_entry_set_positions (entry, current_pos, selection_bound);
+ 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)
{
@@ -5453,6 +5494,9 @@ buffer_deleted_text (GtkEntryBuffer *buffer,
/* We might have deleted the selection */
gtk_entry_update_primary_selection (entry);
+ if (priv->undo_mode == GTK_ENTRY_UNDO_RESET)
+ gtk_undo_stack_clear (priv->undo_stack);
+
/* Disable the password hint if one exists. */
if (!priv->visible)
{
@@ -5764,6 +5808,7 @@ gtk_entry_backspace (GtkEntry *entry)
{
GtkEntryPrivate *priv = entry->priv;
GtkEditable *editable = GTK_EDITABLE (entry);
+ GtkEntryUndoMode old_mode;
gint prev_pos;
gtk_entry_reset_im_context (entry);
@@ -5776,7 +5821,9 @@ gtk_entry_backspace (GtkEntry *entry)
if (priv->selection_bound != priv->current_pos)
{
+ old_mode = gtk_entry_set_undo_mode (entry, GTK_ENTRY_UNDO_RECORD);
gtk_editable_delete_selection (editable);
+ gtk_entry_set_undo_mode (entry, old_mode);
return;
}
@@ -5788,6 +5835,8 @@ gtk_entry_backspace (GtkEntry *entry)
PangoLogAttr *log_attrs;
gint n_attrs;
+ old_mode = gtk_entry_set_undo_mode (entry, GTK_ENTRY_UNDO_RECORD);
+
pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
/* Deleting parts of characters */
@@ -5822,6 +5871,8 @@ gtk_entry_backspace (GtkEntry *entry)
{
gtk_editable_delete_text (editable, prev_pos, priv->current_pos);
}
+
+ gtk_entry_set_undo_mode (entry, old_mode);
g_free (log_attrs);
}
@@ -5862,6 +5913,7 @@ gtk_entry_cut_clipboard (GtkEntry *entry)
{
GtkEntryPrivate *priv = entry->priv;
GtkEditable *editable = GTK_EDITABLE (entry);
+ GtkEntryUndoMode old_mode;
gint start, end;
if (!priv->visible)
@@ -5875,7 +5927,11 @@ gtk_entry_cut_clipboard (GtkEntry *entry)
if (priv->editable)
{
if (gtk_editable_get_selection_bounds (editable, &start, &end))
- gtk_editable_delete_text (editable, start, end);
+ {
+ old_mode = gtk_entry_set_undo_mode (entry, GTK_ENTRY_UNDO_RECORD);
+ gtk_editable_delete_text (editable, start, end);
+ gtk_entry_set_undo_mode (entry, old_mode);
+ }
}
else
{
@@ -6044,12 +6100,17 @@ gtk_entry_delete_surrounding_cb (GtkIMContext *slave,
GtkEntry *entry)
{
GtkEntryPrivate *priv = entry->priv;
+ GtkEntryUndoMode old_mode;
+
+ old_mode = gtk_entry_set_undo_mode (entry, GTK_ENTRY_UNDO_RECORD);
if (priv->editable)
gtk_editable_delete_text (GTK_EDITABLE (entry),
priv->current_pos + offset,
priv->current_pos + offset + n_chars);
+ gtk_entry_set_undo_mode (entry, old_mode);
+
return TRUE;
}
@@ -6063,12 +6124,15 @@ gtk_entry_enter_text (GtkEntry *entry,
{
GtkEntryPrivate *priv = entry->priv;
GtkEditable *editable = GTK_EDITABLE (entry);
+ GtkEntryUndoMode old_mode;
gint tmp_pos;
gboolean old_need_im_reset;
old_need_im_reset = priv->need_im_reset;
priv->need_im_reset = FALSE;
+ old_mode = gtk_entry_set_undo_mode (entry, GTK_ENTRY_UNDO_RECORD);
+
if (gtk_editable_get_selection_bounds (editable, NULL, NULL))
gtk_editable_delete_selection (editable);
else
@@ -6081,6 +6145,8 @@ gtk_entry_enter_text (GtkEntry *entry,
gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
gtk_editable_set_position (editable, tmp_pos);
+ gtk_entry_set_undo_mode (entry, old_mode);
+
priv->need_im_reset = old_need_im_reset;
}
@@ -7329,6 +7395,7 @@ paste_received (GtkClipboard *clipboard,
GtkEntry *entry = GTK_ENTRY (data);
GtkEditable *editable = GTK_EDITABLE (entry);
GtkEntryPrivate *priv = entry->priv;
+ GtkEntryUndoMode old_mode;
guint button;
button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (priv->multipress_gesture));
@@ -7368,12 +7435,14 @@ paste_received (GtkClipboard *clipboard,
}
begin_change (entry);
+ old_mode = gtk_entry_set_undo_mode (entry, GTK_ENTRY_UNDO_RECORD);
if (gtk_editable_get_selection_bounds (editable, &start, &end))
gtk_editable_delete_text (editable, start, end);
pos = priv->current_pos;
gtk_editable_insert_text (editable, text, length, &pos);
gtk_editable_set_position (editable, pos);
+ gtk_entry_set_undo_mode (entry, old_mode);
end_change (entry);
if (completion &&
@@ -10956,6 +11025,17 @@ keymap_state_changed (GdkKeymap *keymap,
remove_capslock_feedback (entry);
}
+GtkEntryUndoMode
+gtk_entry_set_undo_mode (GtkEntry *entry,
+ GtkEntryUndoMode mode)
+{
+ GtkEntryUndoMode result = entry->priv->undo_mode;
+
+ entry->priv->undo_mode = mode;
+
+ return result;
+}
+
/*
* _gtk_entry_set_is_cell_renderer:
* @entry: a #GtkEntry
diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h
index 6d85d87739..3cebcd479f 100644
--- a/gtk/gtkentryprivate.h
+++ b/gtk/gtkentryprivate.h
@@ -26,6 +26,12 @@
G_BEGIN_DECLS
+typedef enum {
+ GTK_ENTRY_UNDO_RESET,
+ GTK_ENTRY_UNDO_RECORD,
+ GTK_ENTRY_UNDO_REPLAY
+} GtkEntryUndoMode;
+
struct _GtkEntryCompletionPrivate
{
GtkWidget *entry;
@@ -79,6 +85,9 @@ void _gtk_entry_completion_popdown (GtkEntryCompletion *completion);
void _gtk_entry_completion_connect (GtkEntryCompletion *completion,
GtkEntry *entry);
void _gtk_entry_completion_disconnect (GtkEntryCompletion *completion);
+GtkEntryUndoMode
+ gtk_entry_set_undo_mode (GtkEntry *entry,
+ GtkEntryUndoMode mode);
gchar* _gtk_entry_get_display_text (GtkEntry *entry,
gint start_pos,
diff --git a/gtk/gtkentryundocommand.c b/gtk/gtkentryundocommand.c
new file mode 100644
index 0000000000..e9fa703aca
--- /dev/null
+++ b/gtk/gtkentryundocommand.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2015 Red Hat Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gtkentryundocommandprivate.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "gtkentry.h"
+#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 */
+};
+
+G_DEFINE_TYPE_WITH_CODE (GtkEntryUndoCommand, gtk_entry_undo_command, GTK_TYPE_UNDO_COMMAND,
+ G_ADD_PRIVATE (GtkEntryUndoCommand))
+
+static gboolean
+gtk_entry_undo_command_run (GtkEntry *entry,
+ int position,
+ int chars_to_delete,
+ const char *text_to_insert)
+{
+ GtkEntryUndoMode old_mode;
+
+ if (entry == NULL)
+ return FALSE;
+
+ 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_undo_mode (entry, old_mode);
+
+ return TRUE;
+}
+
+static gboolean
+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);
+}
+
+gboolean
+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);
+}
+
+GtkUndoCommand *
+gtk_entry_undo_command_merge (GtkUndoCommand *command,
+ GtkUndoCommand *followup)
+{
+ return NULL;
+}
+
+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);
+ else
+ return g_strdup (_("Did nothing"));
+}
+
+static void
+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);
+
+ G_OBJECT_CLASS (gtk_entry_undo_command_parent_class)->finalize (object);
+}
+
+static void
+gtk_entry_undo_command_class_init (GtkEntryUndoCommandClass *klass)
+{
+ GtkUndoCommandClass *undo_class = GTK_UNDO_COMMAND_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_entry_undo_command_finalize;
+
+ undo_class->undo = gtk_entry_undo_command_undo;
+ undo_class->redo = gtk_entry_undo_command_redo;
+ undo_class->merge = gtk_entry_undo_command_merge;
+ undo_class->describe = gtk_entry_undo_command_describe;
+}
+
+static void
+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)
+{
+ GtkEntryUndoCommand *result;
+ GtkEntryUndoCommandPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+ result = g_object_new (GTK_TYPE_ENTRY_UNDO_COMMAND, NULL);
+
+ 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);
+
+ return GTK_UNDO_COMMAND (result);
+}
+
diff --git a/gtk/gtkentryundocommandprivate.h b/gtk/gtkentryundocommandprivate.h
new file mode 100644
index 0000000000..23b69a648e
--- /dev/null
+++ b/gtk/gtkentryundocommandprivate.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2015 Red Hat Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_ENTRY_UNDO_COMMAND_PRIVATE_H__
+#define __GTK_ENTRY_UNDO_COMMAND_PRIVATE_H__
+
+#include <gtk/gtkentry.h>
+#include <gtk/gtkundocommandprivate.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_ENTRY_UNDO_COMMAND (gtk_entry_undo_command_get_type ())
+#define GTK_ENTRY_UNDO_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_ENTRY_UNDO_COMMAND,
GtkEntryUndoCommand))
+#define GTK_ENTRY_UNDO_COMMAND_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_ENTRY_UNDO_COMMAND,
GtkEntryUndoCommandClass))
+#define GTK_IS_ENTRY_UNDO_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_ENTRY_UNDO_COMMAND))
+#define GTK_IS_ENTRY_UNDO_COMMAND_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_ENTRY_UNDO_COMMAND))
+#define GTK_ENTRY_UNDO_COMMAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GTK_TYPE_ENTRY_UNDO_COMMAND, GtkEntryUndoCommandClass))
+
+typedef struct _GtkEntryUndoCommand GtkEntryUndoCommand;
+typedef struct _GtkEntryUndoCommandClass GtkEntryUndoCommandClass;
+
+struct _GtkEntryUndoCommand
+{
+ GtkUndoCommand parent;
+};
+
+struct _GtkEntryUndoCommandClass
+{
+ GtkUndoCommandClass parent_class;
+};
+
+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);
+
+G_END_DECLS
+
+#endif /* __GTK_ENTRY_UNDO_COMMAND_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]