Merge branch 'wip/chergert/fix-6133-commit-funcs' into 'main'
textbuffer: Add GtkTextBufferCommitNotify Closes #6133 See merge request GNOME/gtk!7524
This commit is contained in:
@@ -1880,4 +1880,27 @@ typedef enum {
|
||||
GTK_FONT_RENDERING_MANUAL,
|
||||
} GtkFontRendering;
|
||||
|
||||
/**
|
||||
* GtkTextBufferNotifyFlags:
|
||||
* @GTK_TEXT_BUFFER_NOTIFY_BEFORE_INSERT: Be notified before text
|
||||
* is inserted into the underlying buffer.
|
||||
* @GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT: Be notified after text
|
||||
* has been inserted into the underlying buffer.
|
||||
* @GTK_TEXT_BUFFER_NOTIFY_BEFORE_DELETE: Be notified before text
|
||||
* is deleted from the underlying buffer.
|
||||
* @GTK_TEXT_BUFFER_NOTIFY_AFTER_DELETE: Be notified after text
|
||||
* has been deleted from the underlying buffer.
|
||||
*
|
||||
* Values for [callback@Gtk.TextBufferCommitNotify] to denote the
|
||||
* point of the notification.
|
||||
*
|
||||
* Since: 4.16
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_TEXT_BUFFER_NOTIFY_BEFORE_INSERT = 1 << 0,
|
||||
GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT = 1 << 1,
|
||||
GTK_TEXT_BUFFER_NOTIFY_BEFORE_DELETE = 1 << 2,
|
||||
GTK_TEXT_BUFFER_NOTIFY_AFTER_DELETE = 1 << 3,
|
||||
} GtkTextBufferNotifyFlags;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -68,6 +68,9 @@ struct _GtkTextBufferPrivate
|
||||
|
||||
GtkTextHistory *history;
|
||||
|
||||
GArray *commit_funcs;
|
||||
guint last_commit_handler;
|
||||
|
||||
guint user_action_count;
|
||||
|
||||
/* Whether the buffer has been modified since last save */
|
||||
@@ -75,6 +78,7 @@ struct _GtkTextBufferPrivate
|
||||
guint has_selection : 1;
|
||||
guint can_undo : 1;
|
||||
guint can_redo : 1;
|
||||
guint in_commit_notify : 1;
|
||||
};
|
||||
|
||||
typedef struct _ClipboardRequest ClipboardRequest;
|
||||
@@ -87,6 +91,15 @@ struct _ClipboardRequest
|
||||
guint replace_selection : 1;
|
||||
};
|
||||
|
||||
typedef struct _CommitFunc
|
||||
{
|
||||
GtkTextBufferCommitNotify callback;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_data_destroy;
|
||||
GtkTextBufferNotifyFlags flags;
|
||||
guint handler_id;
|
||||
} CommitFunc;
|
||||
|
||||
enum {
|
||||
INSERT_TEXT,
|
||||
INSERT_PAINTABLE,
|
||||
@@ -151,6 +164,10 @@ static void gtk_text_buffer_real_mark_set (GtkTextBuffer *buffe
|
||||
GtkTextMark *mark);
|
||||
static void gtk_text_buffer_real_undo (GtkTextBuffer *buffer);
|
||||
static void gtk_text_buffer_real_redo (GtkTextBuffer *buffer);
|
||||
static void gtk_text_buffer_commit_notify (GtkTextBuffer *buffer,
|
||||
GtkTextBufferNotifyFlags flags,
|
||||
guint position,
|
||||
guint length);
|
||||
|
||||
static GtkTextBTree* get_btree (GtkTextBuffer *buffer);
|
||||
static void free_log_attr_cache (GtkTextLogAttrCache *cache);
|
||||
@@ -1081,6 +1098,8 @@ gtk_text_buffer_finalize (GObject *object)
|
||||
|
||||
remove_all_selection_clipboards (buffer);
|
||||
|
||||
g_clear_pointer (&buffer->priv->commit_funcs, g_array_unref);
|
||||
|
||||
g_clear_object (&buffer->priv->history);
|
||||
|
||||
if (priv->tag_table)
|
||||
@@ -1198,7 +1217,29 @@ gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
|
||||
text,
|
||||
len);
|
||||
|
||||
_gtk_text_btree_insert (iter, text, len);
|
||||
if (buffer->priv->commit_funcs == NULL)
|
||||
{
|
||||
_gtk_text_btree_insert (iter, text, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
guint position;
|
||||
guint n_chars;
|
||||
|
||||
if (len < 0)
|
||||
len = strlen (text);
|
||||
|
||||
position = gtk_text_iter_get_offset (iter);
|
||||
n_chars = g_utf8_strlen (text, len);
|
||||
|
||||
gtk_text_buffer_commit_notify (buffer,
|
||||
GTK_TEXT_BUFFER_NOTIFY_BEFORE_INSERT,
|
||||
position, n_chars);
|
||||
_gtk_text_btree_insert (iter, text, len);
|
||||
gtk_text_buffer_commit_notify (buffer,
|
||||
GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT,
|
||||
position, n_chars);
|
||||
}
|
||||
|
||||
g_signal_emit (buffer, signals[CHANGED], 0);
|
||||
g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_CURSOR_POSITION]);
|
||||
@@ -1211,6 +1252,7 @@ gtk_text_buffer_emit_insert (GtkTextBuffer *buffer,
|
||||
int len)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
|
||||
g_return_if_fail (buffer->priv->in_commit_notify == FALSE);
|
||||
g_return_if_fail (iter != NULL);
|
||||
g_return_if_fail (text != NULL);
|
||||
|
||||
@@ -1955,7 +1997,36 @@ gtk_text_buffer_real_delete_range (GtkTextBuffer *buffer,
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
_gtk_text_btree_delete (start, end);
|
||||
|
||||
|
||||
if (buffer->priv->commit_funcs == NULL)
|
||||
{
|
||||
_gtk_text_btree_delete (start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
guint off1 = gtk_text_iter_get_offset (start);
|
||||
guint off2 = gtk_text_iter_get_offset (end);
|
||||
|
||||
if (off2 < off1)
|
||||
{
|
||||
guint tmp = off1;
|
||||
off1 = off2;
|
||||
off2 = tmp;
|
||||
}
|
||||
|
||||
buffer->priv->in_commit_notify = TRUE;
|
||||
|
||||
gtk_text_buffer_commit_notify (buffer,
|
||||
GTK_TEXT_BUFFER_NOTIFY_BEFORE_DELETE,
|
||||
off1, off2 - off1);
|
||||
_gtk_text_btree_delete (start, end);
|
||||
gtk_text_buffer_commit_notify (buffer,
|
||||
GTK_TEXT_BUFFER_NOTIFY_AFTER_DELETE,
|
||||
off1, 0);
|
||||
|
||||
buffer->priv->in_commit_notify = FALSE;
|
||||
}
|
||||
|
||||
/* may have deleted the selection... */
|
||||
update_selection_clipboards (buffer);
|
||||
@@ -1979,6 +2050,7 @@ gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
|
||||
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
|
||||
g_return_if_fail (start != NULL);
|
||||
g_return_if_fail (end != NULL);
|
||||
g_return_if_fail (buffer->priv->in_commit_notify == FALSE);
|
||||
|
||||
if (gtk_text_iter_equal (start, end))
|
||||
return;
|
||||
@@ -5720,3 +5792,130 @@ gtk_text_buffer_add_run_attributes (GtkTextBuffer *buffer,
|
||||
|
||||
g_slist_free (tags);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_commit_func (gpointer data)
|
||||
{
|
||||
CommitFunc *func = data;
|
||||
|
||||
if (func->user_data_destroy)
|
||||
func->user_data_destroy (func->user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_text_buffer_add_commit_notify:
|
||||
* @buffer: a [type@Gtk.TextBuffer]
|
||||
* @flags: which notifications should be dispatched to @callback
|
||||
* @commit_notify: (scope async) (closure user_data) (destroy destroy): a
|
||||
* [callback@Gtk.TextBufferCommitNotify] to call for commit notifications
|
||||
* @user_data: closure data for @commit_notify
|
||||
* @destroy: a callback to free @user_data when @commit_notify is removed
|
||||
*
|
||||
* Adds a [callback@Gtk.TextBufferCommitNotify] to be called when a change
|
||||
* is to be made to the [type@Gtk.TextBuffer].
|
||||
*
|
||||
* Functions are explicitly forbidden from making changes to the
|
||||
* [type@Gtk.TextBuffer] from this callback. It is intended for tracking
|
||||
* changes to the buffer only.
|
||||
*
|
||||
* It may be advantageous to use [callback@Gtk.TextBufferCommitNotify] over
|
||||
* connecting to the [signal@Gtk.TextBuffer::insert-text] or
|
||||
* [signal@Gtk.TextBuffer::delete-range] signals to avoid ordering issues with
|
||||
* other signal handlers which may further modify the [type@Gtk.TextBuffer].
|
||||
*
|
||||
* Returns: a handler id which may be used to remove the commit notify
|
||||
* callback using [method@Gtk.TextBuffer.remove_commit_notify].
|
||||
*
|
||||
* Since: 4.16
|
||||
*/
|
||||
guint
|
||||
gtk_text_buffer_add_commit_notify (GtkTextBuffer *buffer,
|
||||
GtkTextBufferNotifyFlags flags,
|
||||
GtkTextBufferCommitNotify commit_notify,
|
||||
gpointer user_data,
|
||||
GDestroyNotify destroy)
|
||||
{
|
||||
CommitFunc func;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), 0);
|
||||
g_return_val_if_fail (buffer->priv->in_commit_notify == FALSE, 0);
|
||||
|
||||
func.callback = commit_notify;
|
||||
func.user_data = user_data;
|
||||
func.user_data_destroy = destroy;
|
||||
func.handler_id = ++buffer->priv->last_commit_handler;
|
||||
func.flags = flags;
|
||||
|
||||
if (buffer->priv->commit_funcs == NULL)
|
||||
{
|
||||
buffer->priv->commit_funcs = g_array_new (FALSE, FALSE, sizeof (CommitFunc));
|
||||
g_array_set_clear_func (buffer->priv->commit_funcs, clear_commit_func);
|
||||
}
|
||||
|
||||
g_array_append_val (buffer->priv->commit_funcs, func);
|
||||
|
||||
return func.handler_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_text_buffer_remove_commit_notify:
|
||||
* @buffer: a `GtkTextBuffer`
|
||||
* @commit_notify_handler: the notify handler identifier returned from
|
||||
* [method@Gtk.TextBuffer.add_commit_notify].
|
||||
*
|
||||
* Removes the `GtkTextBufferCommitNotify` handler previously registered
|
||||
* with [method@Gtk.TextBuffer.add_commit_notify].
|
||||
*
|
||||
* This may result in the `user_data_destroy` being called that was passed when registering
|
||||
* the commit notify functions.
|
||||
*
|
||||
* Since: 4.16
|
||||
*/
|
||||
void
|
||||
gtk_text_buffer_remove_commit_notify (GtkTextBuffer *buffer,
|
||||
guint commit_notify_handler)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
|
||||
g_return_if_fail (commit_notify_handler > 0);
|
||||
g_return_if_fail (buffer->priv->in_commit_notify == FALSE);
|
||||
|
||||
if (buffer->priv->commit_funcs != NULL)
|
||||
{
|
||||
for (guint i = 0; i < buffer->priv->commit_funcs->len; i++)
|
||||
{
|
||||
const CommitFunc *func = &g_array_index (buffer->priv->commit_funcs, CommitFunc, i);
|
||||
|
||||
if (func->handler_id == commit_notify_handler)
|
||||
{
|
||||
g_array_remove_index (buffer->priv->commit_funcs, i);
|
||||
|
||||
if (buffer->priv->commit_funcs->len == 0)
|
||||
g_clear_pointer (&buffer->priv->commit_funcs, g_array_unref);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_warning ("No such GtkTextBufferCommitNotify matching %u",
|
||||
commit_notify_handler);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_buffer_commit_notify (GtkTextBuffer *buffer,
|
||||
GtkTextBufferNotifyFlags flags,
|
||||
guint position,
|
||||
guint length)
|
||||
{
|
||||
buffer->priv->in_commit_notify = TRUE;
|
||||
|
||||
for (guint i = 0; i < buffer->priv->commit_funcs->len; i++)
|
||||
{
|
||||
const CommitFunc *func = &g_array_index (buffer->priv->commit_funcs, CommitFunc, i);
|
||||
|
||||
if (func->flags & flags)
|
||||
func->callback (buffer, flags, position, length, func->user_data);
|
||||
}
|
||||
|
||||
buffer->priv->in_commit_notify = FALSE;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,55 @@ struct _GtkTextBuffer
|
||||
GtkTextBufferPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* GtkTextBufferCommitNotify:
|
||||
* @buffer: the text buffer being notified
|
||||
* @flags: the type of commit notification
|
||||
* @position: the position of the text operation
|
||||
* @length: the length of the text operation in characters
|
||||
* @user_data: (closure): user data passed to the callback
|
||||
*
|
||||
* A notification callback used by [method@Gtk.TextBuffer.add_commit_notify].
|
||||
*
|
||||
* You may not modify the [class@Gtk.TextBuffer] from a
|
||||
* [callback@Gtk.TextBufferCommitNotify] callback and that is enforced
|
||||
* by the [class@Gtk.TextBuffer] API.
|
||||
*
|
||||
* [callback@Gtk.TextBufferCommitNotify] may be used to be notified about
|
||||
* changes to the underlying buffer right before-or-after the changes are
|
||||
* committed to the underlying B-Tree. This is useful if you want to observe
|
||||
* changes to the buffer without other signal handlers potentially modifying
|
||||
* state on the way to the default signal handler.
|
||||
*
|
||||
* When @flags is `GTK_TEXT_BUFFER_NOTIFY_BEFORE_INSERT`, `position` is set to
|
||||
* the offset in characters from the start of the buffer where the insertion
|
||||
* will occur. `length` is set to the number of characters to be inserted. You
|
||||
* may not yet retrieve the text until it has been inserted. You may access the
|
||||
* text from `GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT` using
|
||||
* [method@Gtk.TextBuffer.get_slice].
|
||||
*
|
||||
* When @flags is `GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT`, `position` is set to
|
||||
* offset in characters where the insertion occurred and `length` is set
|
||||
* to the number of characters inserted.
|
||||
*
|
||||
* When @flags is `GTK_TEXT_BUFFER_NOTIFY_BEFORE_DELETE`, `position` is set to
|
||||
* offset in characters where the deletion will occur and `length` is set
|
||||
* to the number of characters that will be removed. You may still retrieve
|
||||
* the text from this handler using `position` and `length`.
|
||||
*
|
||||
* When @flags is `GTK_TEXT_BUFFER_NOTIFY_AFTER_DELETE`, `length` is set to
|
||||
* zero to denote that the delete-range has already been committed to the
|
||||
* underlying B-Tree. You may no longer retrieve the text that has been
|
||||
* deleted from the [class@Gtk.TextBuffer].
|
||||
*
|
||||
* Since: 4.16
|
||||
*/
|
||||
typedef void (*GtkTextBufferCommitNotify) (GtkTextBuffer *buffer,
|
||||
GtkTextBufferNotifyFlags flags,
|
||||
guint position,
|
||||
guint length,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GtkTextBufferClass:
|
||||
* @parent_class: The object class structure needs to be the first.
|
||||
@@ -459,6 +508,15 @@ 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);
|
||||
GDK_AVAILABLE_IN_4_16
|
||||
guint gtk_text_buffer_add_commit_notify (GtkTextBuffer *buffer,
|
||||
GtkTextBufferNotifyFlags flags,
|
||||
GtkTextBufferCommitNotify commit_notify,
|
||||
gpointer user_data,
|
||||
GDestroyNotify destroy);
|
||||
GDK_AVAILABLE_IN_4_16
|
||||
void gtk_text_buffer_remove_commit_notify (GtkTextBuffer *buffer,
|
||||
guint commit_notify_handler);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTextBuffer, g_object_unref)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user