mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-15 13:10:08 +00:00
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:
parent
5e341210a1
commit
15b3c0f563
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user