textview: add undo/redo support to GtkTextView

This builds upon the GtkTextHistory helper to provide undo and redo support
for the GtkTextView widget and GtkTextBuffer object.

You can undo/redo using familiar shortcuts such as Primary+Z,
Primary+Shift+Z, ad Primary+Y.

Developers that wish to disable undo, should set the
GtkTextBuffer:enable-undo property to FALSE.

You can wrap irreversible actions
gtk_text_buffer_begin_irreversible_action() and
gtk_text_buffer_end_irreversible_action(). This will cause the undo stack
to drop all undo/redo actions and the changes made between them will be
the "initial state" of the buffer.

Calling gtk_text_buffer_set_text() will do this automatically for you.
This commit is contained in:
Christian Hergert 2019-11-01 11:12:21 -07:00
parent 5e341210a1
commit 15b3c0f563
6 changed files with 509 additions and 10 deletions

View File

@ -2876,6 +2876,18 @@ gtk_text_buffer_begin_user_action
gtk_text_buffer_end_user_action
gtk_text_buffer_add_selection_clipboard
gtk_text_buffer_remove_selection_clipboard
gtk_text_buffer_get_can_undo
gtk_text_buffer_get_can_redo
gtk_text_buffer_get_enable_undo
gtk_text_buffer_set_enable_undo
gtk_text_buffer_get_max_undo_levels
gtk_text_buffer_set_max_undo_levels
gtk_text_buffer_undo
gtk_text_buffer_redo
gtk_text_buffer_begin_irreversible_action
gtk_text_buffer_end_irreversible_action
gtk_text_buffer_begin_user_action
gtk_text_buffer_end_user_action
<SUBSECTION Standard>
GTK_TEXT_BUFFER

View File

@ -112,6 +112,16 @@ multiple bytes in UTF-8, and the two-character sequence "\r\n" is also
considered a line separator.
</para>
<para>
Text buffers support undo and redo if gtk_text_buffer_set_undo_enabled()
has been set to %TRUE. Use gtk_text_buffer_undo() or gtk_text_buffer_redo()
to perform the necessary action. Note that these operations are ignored if
the buffer is not editable. Developers may want some operations to not be
undoable. To do this, wrap your changes in
gtk_text_buffer_begin_irreversible_action() and
gtk_text_buffer_end_irreversible_action().
</para>
</refsect1>

View File

@ -30,6 +30,7 @@
#include "gtkdnd.h"
#include "gtkmarshalers.h"
#include "gtktextbuffer.h"
#include "gtktexthistoryprivate.h"
#include "gtktextbufferprivate.h"
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"
@ -38,6 +39,8 @@
#include "gtkprivate.h"
#include "gtkintl.h"
#define DEFAULT_MAX_UNDO 200
/**
* SECTION:gtktextbuffer
* @Short_description: Stores attributed text for display in a GtkTextView
@ -62,11 +65,15 @@ struct _GtkTextBufferPrivate
GtkTextLogAttrCache *log_attr_cache;
GtkTextHistory *history;
guint user_action_count;
/* Whether the buffer has been modified since last save */
guint modified : 1;
guint has_selection : 1;
guint can_undo : 1;
guint can_redo : 1;
};
typedef struct _ClipboardRequest ClipboardRequest;
@ -93,6 +100,8 @@ enum {
BEGIN_USER_ACTION,
END_USER_ACTION,
PASTE_DONE,
UNDO,
REDO,
LAST_SIGNAL
};
@ -108,6 +117,9 @@ enum {
PROP_CURSOR_POSITION,
PROP_COPY_TARGET_LIST,
PROP_PASTE_TARGET_LIST,
PROP_CAN_UNDO,
PROP_CAN_REDO,
PROP_ENABLE_UNDO,
LAST_PROP
};
@ -138,6 +150,8 @@ static void gtk_text_buffer_real_changed (GtkTextBuffer *buffe
static void gtk_text_buffer_real_mark_set (GtkTextBuffer *buffer,
const GtkTextIter *iter,
GtkTextMark *mark);
static void gtk_text_buffer_real_undo (GtkTextBuffer *buffer);
static void gtk_text_buffer_real_redo (GtkTextBuffer *buffer);
static GtkTextBTree* get_btree (GtkTextBuffer *buffer);
static void free_log_attr_cache (GtkTextLogAttrCache *cache);
@ -154,6 +168,24 @@ static void gtk_text_buffer_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
static void gtk_text_buffer_history_change_state (gpointer funcs_data,
gboolean is_modified,
gboolean can_undo,
gboolean can_redo);
static void gtk_text_buffer_history_insert (gpointer funcs_data,
guint begin,
guint end,
const char *text,
guint len);
static void gtk_text_buffer_history_delete (gpointer funcs_data,
guint begin,
guint end,
const char *expected_text,
guint len);
static void gtk_text_buffer_history_select (gpointer funcs_data,
int selection_insert,
int selection_bound);
static guint signals[LAST_SIGNAL] = { 0 };
static GParamSpec *text_buffer_props[LAST_PROP];
@ -185,6 +217,13 @@ GType gtk_text_buffer_content_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE (GtkTextBufferContent, gtk_text_buffer_content, GDK_TYPE_CONTENT_PROVIDER)
static GtkTextHistoryFuncs history_funcs = {
gtk_text_buffer_history_change_state,
gtk_text_buffer_history_insert,
gtk_text_buffer_history_delete,
gtk_text_buffer_history_select,
};
static GdkContentFormats *
gtk_text_buffer_content_ref_formats (GdkContentProvider *provider)
{
@ -403,6 +442,8 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass)
klass->remove_tag = gtk_text_buffer_real_remove_tag;
klass->changed = gtk_text_buffer_real_changed;
klass->mark_set = gtk_text_buffer_real_mark_set;
klass->undo = gtk_text_buffer_real_undo;
klass->redo = gtk_text_buffer_real_redo;
/* Construct */
text_buffer_props[PROP_TAG_TABLE] =
@ -439,6 +480,45 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass)
FALSE,
GTK_PARAM_READABLE);
/**
* GtkTextBuffer:can-undo:
*
* The :can-undo property denotes that the buffer can undo the last
* applied action.
*/
text_buffer_props[PROP_CAN_UNDO] =
g_param_spec_boolean ("can-undo",
P_("Can Undo"),
P_("If the buffer can have the last action undone"),
FALSE,
GTK_PARAM_READABLE);
/**
* GtkTextBuffer:can-redo:
*
* The :can-redo property denotes that the buffer can reapply the
* last undone action.
*/
text_buffer_props[PROP_CAN_REDO] =
g_param_spec_boolean ("can-redo",
P_("Can Redo"),
P_("If the buffer can have the last undone action reapplied"),
FALSE,
GTK_PARAM_READABLE);
/**
* GtkTextBuffer:enable-undo:
*
* The :enable-undo property denotes if support for undoing and redoing
* changes to the buffer is allowed.
*/
text_buffer_props[PROP_ENABLE_UNDO] =
g_param_spec_boolean ("enable-undo",
"Enable Undo",
"Enable support for undo and redo in the text view",
TRUE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkTextBuffer:cursor-position:
*
@ -840,6 +920,34 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass)
1,
GDK_TYPE_CLIPBOARD);
/**
* GtkTextBuffer::redo:
* @buffer: a #GtkTextBuffer
*
* The "redo" signal is emitted when a request has been made to redo the
* previously undone operation.
*/
signals[REDO] =
g_signal_new (I_("redo"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkTextBufferClass, redo),
NULL, NULL, NULL, G_TYPE_NONE, 0);
/**
* GtkTextBuffer::undo:
* @buffer: a #GtkTextBuffer
*
* The "undo" signal is emitted when a request has been made to undo the
* previous operation or set of operations that have been grouped together.
*/
signals[UNDO] =
g_signal_new (I_("undo"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkTextBufferClass, undo),
NULL, NULL, NULL, G_TYPE_NONE, 0);
gtk_text_buffer_register_serializers ();
}
@ -848,6 +956,9 @@ gtk_text_buffer_init (GtkTextBuffer *buffer)
{
buffer->priv = gtk_text_buffer_get_instance_private (buffer);
buffer->priv->tag_table = NULL;
buffer->priv->history = gtk_text_history_new (&history_funcs, buffer);
gtk_text_history_set_max_undo_levels (buffer->priv->history, DEFAULT_MAX_UNDO);
}
static void
@ -891,6 +1002,10 @@ gtk_text_buffer_set_property (GObject *object,
switch (prop_id)
{
case PROP_ENABLE_UNDO:
gtk_text_buffer_set_enable_undo (text_buffer, g_value_get_boolean (value));
break;
case PROP_TAG_TABLE:
set_table (text_buffer, g_value_get_object (value));
break;
@ -919,6 +1034,10 @@ gtk_text_buffer_get_property (GObject *object,
switch (prop_id)
{
case PROP_ENABLE_UNDO:
g_value_set_boolean (value, gtk_text_buffer_get_enable_undo (text_buffer));
break;
case PROP_TAG_TABLE:
g_value_set_object (value, get_table (text_buffer));
break;
@ -946,6 +1065,14 @@ gtk_text_buffer_get_property (GObject *object,
g_value_set_int (value, gtk_text_iter_get_offset (&iter));
break;
case PROP_CAN_UNDO:
g_value_set_boolean (value, gtk_text_buffer_get_can_undo (text_buffer));
break;
case PROP_CAN_REDO:
g_value_set_boolean (value, gtk_text_buffer_get_can_redo (text_buffer));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -981,6 +1108,8 @@ gtk_text_buffer_finalize (GObject *object)
remove_all_selection_clipboards (buffer);
g_clear_object (&buffer->priv->history);
if (priv->tag_table)
{
_gtk_text_tag_table_remove_buffer (priv->tag_table, buffer);
@ -1058,6 +1187,8 @@ gtk_text_buffer_set_text (GtkTextBuffer *buffer,
if (len < 0)
len = strlen (text);
gtk_text_history_begin_irreversible_action (buffer->priv->history);
gtk_text_buffer_get_bounds (buffer, &start, &end);
gtk_text_buffer_delete (buffer, &start, &end);
@ -1067,6 +1198,8 @@ gtk_text_buffer_set_text (GtkTextBuffer *buffer,
gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
gtk_text_buffer_insert (buffer, &start, text, len);
}
gtk_text_history_end_irreversible_action (buffer->priv->history);
}
@ -1084,6 +1217,11 @@ gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
g_return_if_fail (iter != NULL);
gtk_text_history_text_inserted (buffer->priv->history,
gtk_text_iter_get_offset (iter),
text,
len);
_gtk_text_btree_insert (iter, text, len);
g_signal_emit (buffer, signals[CHANGED], 0);
@ -1798,6 +1936,28 @@ gtk_text_buffer_real_delete_range (GtkTextBuffer *buffer,
g_return_if_fail (start != NULL);
g_return_if_fail (end != NULL);
if (gtk_text_history_get_enabled (buffer->priv->history))
{
GtkTextIter sel_begin, sel_end;
gchar *text;
if (gtk_text_buffer_get_selection_bounds (buffer, &sel_begin, &sel_end))
gtk_text_history_selection_changed (buffer->priv->history,
gtk_text_iter_get_offset (&sel_begin),
gtk_text_iter_get_offset (&sel_end));
else
gtk_text_history_selection_changed (buffer->priv->history,
gtk_text_iter_get_offset (&sel_begin),
-1);
text = gtk_text_iter_get_slice (start, end);
gtk_text_history_text_deleted (buffer->priv->history,
gtk_text_iter_get_offset (start),
gtk_text_iter_get_offset (end),
text, -1);
g_free (text);
}
_gtk_text_btree_delete (start, end);
/* may have deleted the selection... */
@ -3274,17 +3434,14 @@ void
gtk_text_buffer_set_modified (GtkTextBuffer *buffer,
gboolean setting)
{
gboolean fixed_setting;
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
fixed_setting = setting != FALSE;
setting = !!setting;
if (buffer->priv->modified == fixed_setting)
return;
else
if (buffer->priv->modified != setting)
{
buffer->priv->modified = fixed_setting;
buffer->priv->modified = setting;
gtk_text_history_modified_changed (buffer->priv->history, setting);
g_signal_emit (buffer, signals[MODIFIED_CHANGED], 0);
}
}
@ -4723,3 +4880,258 @@ gtk_text_buffer_insert_markup (GtkTextBuffer *buffer,
pango_attr_list_unref (attributes);
g_free (text);
}
static void
gtk_text_buffer_real_undo (GtkTextBuffer *buffer)
{
if (gtk_text_history_get_can_undo (buffer->priv->history))
gtk_text_history_undo (buffer->priv->history);
}
static void
gtk_text_buffer_real_redo (GtkTextBuffer *buffer)
{
if (gtk_text_history_get_can_redo (buffer->priv->history))
gtk_text_history_redo (buffer->priv->history);
}
gboolean
gtk_text_buffer_get_can_undo (GtkTextBuffer *buffer)
{
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
return gtk_text_history_get_can_undo (buffer->priv->history);
}
gboolean
gtk_text_buffer_get_can_redo (GtkTextBuffer *buffer)
{
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
return gtk_text_history_get_can_redo (buffer->priv->history);
}
static void
gtk_text_buffer_history_change_state (gpointer funcs_data,
gboolean is_modified,
gboolean can_undo,
gboolean can_redo)
{
GtkTextBuffer *buffer = funcs_data;
if (buffer->priv->can_undo != can_undo)
{
buffer->priv->can_undo = can_undo;
g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_CAN_UNDO]);
}
if (buffer->priv->can_redo != can_redo)
{
buffer->priv->can_redo = can_redo;
g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_CAN_REDO]);
}
if (buffer->priv->modified != is_modified)
gtk_text_buffer_set_modified (buffer, is_modified);
}
static void
gtk_text_buffer_history_insert (gpointer funcs_data,
guint begin,
guint end,
const char *text,
guint len)
{
GtkTextBuffer *buffer = funcs_data;
GtkTextIter iter;
gtk_text_buffer_get_iter_at_offset (buffer, &iter, begin);
gtk_text_buffer_insert (buffer, &iter, text, len);
}
static void
gtk_text_buffer_history_delete (gpointer funcs_data,
guint begin,
guint end,
const char *expected_text,
guint len)
{
GtkTextBuffer *buffer = funcs_data;
GtkTextIter iter;
GtkTextIter end_iter;
gtk_text_buffer_get_iter_at_offset (buffer, &iter, begin);
gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
gtk_text_buffer_delete (buffer, &iter, &end_iter);
}
static void
gtk_text_buffer_history_select (gpointer funcs_data,
int selection_insert,
int selection_bound)
{
GtkTextBuffer *buffer = funcs_data;
GtkTextIter insert;
GtkTextIter bound;
if (selection_insert == -1 || selection_bound == -1)
return;
gtk_text_buffer_get_iter_at_offset (buffer, &insert, selection_insert);
gtk_text_buffer_get_iter_at_offset (buffer, &bound, selection_bound);
gtk_text_buffer_select_range (buffer, &insert, &bound);
}
/**
* gtk_text_buffer_undo:
* @buffer: a #GtkTextBuffer
*
* Undoes the last undoable action on the buffer, if there is one.
*/
void
gtk_text_buffer_undo (GtkTextBuffer *buffer)
{
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
if (gtk_text_buffer_get_can_undo (buffer))
g_signal_emit (buffer, signals[UNDO], 0);
}
/**
* gtk_text_buffer_redo:
* @buffer: a #GtkTextBuffer
*
* Redoes the next redoable action on the buffer, if there is one.
*/
void
gtk_text_buffer_redo (GtkTextBuffer *buffer)
{
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
if (gtk_text_buffer_get_can_redo (buffer))
g_signal_emit (buffer, signals[REDO], 0);
}
/**
* gtk_text_buffer_get_enable_undo:
* @buffer: a #GtkTextBuffer
*
* Gets whether the buffer is saving modifications to the buffer to allow for
* undo and redo actions.
*
* See gtk_text_buffer_begin_irreversible_action() and
* gtk_text_buffer_end_irreversible_action() to create changes to the buffer
* that cannot be undone.
*/
gboolean
gtk_text_buffer_get_enable_undo (GtkTextBuffer *buffer)
{
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
return gtk_text_history_get_enabled (buffer->priv->history);
}
/**
* gtk_text_buffer_set_enable_undo:
* @buffer: a #GtkTextBuffer
*
* Sets whether or not to enable undoable actions in the text buffer. If
* enabled, the user will be able to undo the last number of actions up to
* gtk_text_buffer_get_max_undo_levels().
*
* See gtk_text_buffer_begin_irreversible_action() and
* gtk_text_buffer_end_irreversible_action() to create changes to the buffer
* that cannot be undone.
*/
void
gtk_text_buffer_set_enable_undo (GtkTextBuffer *buffer,
gboolean enabled)
{
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
if (enabled != gtk_text_history_get_enabled (buffer->priv->history))
{
gtk_text_history_set_enabled (buffer->priv->history, enabled);
g_object_notify_by_pspec (G_OBJECT (buffer),
text_buffer_props[PROP_ENABLE_UNDO]);
}
}
/**
* gtk_text_buffer_begin_irreversible_action:
* @self: a #Gtktextbuffer
*
* Denotes the beginning of an action that may not be undone. This will cause
* any previous operations in the undo/redo queue to be cleared.
*
* This should be paired with a call to
* gtk_text_buffer_end_irreversible_action() after the irreversible action
* has completed.
*
* You may nest calls to gtk_text_buffer_begin_irreversible_action() and
* gtk_text_buffer_end_irreversible_action() pairs.
*/
void
gtk_text_buffer_begin_irreversible_action (GtkTextBuffer *buffer)
{
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
gtk_text_history_begin_irreversible_action (buffer->priv->history);
}
/**
* gtk_text_buffer_end_irreversible_action:
* @self: a #Gtktextbuffer
*
* Denotes the end of an action that may not be undone. This will cause
* any previous operations in the undo/redo queue to be cleared.
*
* This should be called after completing modifications to the text buffer
* after gtk_text_buffer_begin_irreversible_action() was called.
*
* You may nest calls to gtk_text_buffer_begin_irreversible_action() and
* gtk_text_buffer_end_irreversible_action() pairs.
*/
void
gtk_text_buffer_end_irreversible_action (GtkTextBuffer *buffer)
{
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
gtk_text_history_end_irreversible_action (buffer->priv->history);
}
/**
* gtk_text_buffer_get_max_undo_levels:
* @buffer: a #GtkTextBuffer
*
* Gets the maximum number of undo levels to perform. If 0, unlimited undo
* actions may be performed. Note that this may have a memory usage impact
* as it requires storing an additional copy of the inserted or removed text
* within the text buffer.
*/
guint
gtk_text_buffer_get_max_undo_levels (GtkTextBuffer *buffer)
{
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), 0);
return gtk_text_history_get_max_undo_levels (buffer->priv->history);
}
/**
* gtk_text_buffer_set_max_undo_levels:
* @buffer: a #GtkTextBuffer
* @max_undo_levels: the maximum number of undo actions to perform
*
* Sets the maximum number of undo levels to perform. If 0, unlimited undo
* actions may be performed. Note that this may have a memory usage impact
* as it requires storing an additional copy of the inserted or removed text
* within the text buffer.
*/
void
gtk_text_buffer_set_max_undo_levels (GtkTextBuffer *buffer,
guint max_undo_levels)
{
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
gtk_text_history_set_max_undo_levels (buffer->priv->history, max_undo_levels);
}

View File

@ -146,6 +146,8 @@ struct _GtkTextBufferClass
void (* paste_done) (GtkTextBuffer *buffer,
GdkClipboard *clipboard);
void (* undo) (GtkTextBuffer *buffer);
void (* redo) (GtkTextBuffer *buffer);
/*< private >*/
@ -451,11 +453,32 @@ gboolean gtk_text_buffer_delete_selection (GtkTextBuffer *buffer,
gboolean interactive,
gboolean default_editable);
/* Called to specify atomic user actions, used to implement undo */
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_begin_user_action (GtkTextBuffer *buffer);
gboolean gtk_text_buffer_get_can_undo (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_end_user_action (GtkTextBuffer *buffer);
gboolean gtk_text_buffer_get_can_redo (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
gboolean gtk_text_buffer_get_enable_undo (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_set_enable_undo (GtkTextBuffer *buffer,
gboolean enable_undo);
GDK_AVAILABLE_IN_ALL
guint gtk_text_buffer_get_max_undo_levels (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_set_max_undo_levels (GtkTextBuffer *buffer,
guint max_undo_levels);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_undo (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_redo (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_begin_irreversible_action (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_end_irreversible_action (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_begin_user_action (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
void gtk_text_buffer_end_user_action (GtkTextBuffer *buffer);
G_END_DECLS

View File

@ -85,6 +85,8 @@ struct _GtkTextClass
void (* paste_clipboard) (GtkText *self);
void (* toggle_overwrite) (GtkText *self);
void (* insert_emoji) (GtkText *self);
void (* undo) (GtkText *self);
void (* redo) (GtkText *self);
};
char * gtk_text_get_display_text (GtkText *entry,

View File

@ -619,6 +619,13 @@ static void gtk_text_view_activate_misc_insert_emoji (GtkWidget *widget,
const char *action_name,
GVariant *parameter);
static void gtk_text_view_real_undo (GtkWidget *widget,
const gchar *action_name,
GVariant *parameter);
static void gtk_text_view_real_redo (GtkWidget *widget,
const gchar *action_name,
GVariant *parameter);
/* FIXME probably need the focus methods. */
@ -1358,6 +1365,9 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
NULL,
G_TYPE_NONE, 0);
gtk_widget_class_install_action (widget_class, "text.undo", NULL, gtk_text_view_real_undo);
gtk_widget_class_install_action (widget_class, "text.redo", NULL, gtk_text_view_real_redo);
/*
* Key bindings
*/
@ -1550,6 +1560,14 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_SHIFT_MASK,
"paste-clipboard", 0);
/* Undo/Redo */
gtk_binding_entry_add_action (binding_set, GDK_KEY_z, GDK_CONTROL_MASK,
"text.undo", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_y, GDK_CONTROL_MASK,
"text.redo", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
"text.redo", NULL);
/* Overwrite */
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, 0,
"toggle-overwrite", 0);
@ -9835,3 +9853,25 @@ gtk_text_view_get_extra_menu (GtkTextView *text_view)
return priv->extra_menu;
}
static void
gtk_text_view_real_undo (GtkWidget *widget,
const gchar *action_name,
GVariant *parameters)
{
GtkTextView *text_view = GTK_TEXT_VIEW (widget);
if (gtk_text_view_get_editable (text_view))
gtk_text_buffer_undo (text_view->priv->buffer);
}
static void
gtk_text_view_real_redo (GtkWidget *widget,
const gchar *action_name,
GVariant *parameters)
{
GtkTextView *text_view = GTK_TEXT_VIEW (widget);
if (gtk_text_view_get_editable (text_view))
gtk_text_buffer_redo (text_view->priv->buffer);
}