X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktextbuffer.c;h=a877e345995348e4ce5b57406de23301a25f93ef;hb=0ba92bc26d1b716f2f9c0543593f13cd5a92c521;hp=3cea28d37e8ca303b7ff69eb6ec60348af9088dd;hpb=269d89c79c9e8e872b3599242d1e174afeab4b00;p=~andy%2Fgtk diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c index 3cea28d37..a877e3459 100644 --- a/gtk/gtktextbuffer.c +++ b/gtk/gtktextbuffer.c @@ -1,5 +1,6 @@ /* GTK - The GIMP Toolkit * gtktextbuffer.c Copyright (C) 2000 Red Hat, Inc. + * Copyright (C) 2004 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -12,9 +13,7 @@ * 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, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -24,32 +23,72 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ - -#include +#include "config.h" #include #include - #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API #include "gtkclipboard.h" +#include "gtkdnd.h" #include "gtkinvisible.h" #include "gtkmarshalers.h" #include "gtktextbuffer.h" +#include "gtktextbufferrichtext.h" #include "gtktextbtree.h" #include "gtktextiterprivate.h" +#include "gtktexttagprivate.h" #include "gtkprivate.h" #include "gtkintl.h" -#include "gtkalias.h" + + +/** + * SECTION:gtktextbuffer + * @Short_description: Stores attributed text for display in a GtkTextView + * @Title: GtkTextBuffer + * @See_also: #GtkTextView, #GtkTextIter, #GtkTextMark + * + * You may wish to begin by reading the text widget + * conceptual overview which gives an overview of all the objects and data + * types related to the text widget and how they work together. + */ + + +typedef struct _GtkTextLogAttrCache GtkTextLogAttrCache; + +struct _GtkTextBufferPrivate +{ + GtkTargetList *copy_target_list; + GtkTargetEntry *copy_target_entries; + GtkTargetList *paste_target_list; + GtkTargetEntry *paste_target_entries; + + gint n_copy_target_entries; + gint n_paste_target_entries; + + GtkTextTagTable *tag_table; + GtkTextBTree *btree; + + GSList *clipboard_contents_buffers; + GSList *selection_clipboards; + + GtkTextLogAttrCache *log_attr_cache; + + guint user_action_count; + + /* Whether the buffer has been modified since last save */ + guint modified : 1; + guint has_selection : 1; +}; + typedef struct _ClipboardRequest ClipboardRequest; struct _ClipboardRequest { GtkTextBuffer *buffer; - gboolean interactive; - gboolean default_editable; - gboolean is_clipboard; - gboolean replace_selection; + guint interactive : 1; + guint default_editable : 1; + guint replace_selection : 1; }; enum { @@ -65,6 +104,7 @@ enum { REMOVE_TAG, BEGIN_USER_ACTION, END_USER_ACTION, + PASTE_DONE, LAST_SIGNAL }; @@ -72,22 +112,18 @@ enum { PROP_0, /* Construct */ - PROP_TAG_TABLE -}; - -enum { - TARGET_STRING, - TARGET_TEXT, - TARGET_COMPOUND_TEXT, - TARGET_UTF8_STRING, - TARGET_TEXT_BUFFER_CONTENTS + PROP_TAG_TABLE, + + /* Normal */ + PROP_TEXT, + PROP_HAS_SELECTION, + PROP_CURSOR_POSITION, + PROP_COPY_TARGET_LIST, + PROP_PASTE_TARGET_LIST }; -static void gtk_text_buffer_init (GtkTextBuffer *tkxt_buffer); -static void gtk_text_buffer_class_init (GtkTextBufferClass *klass); static void gtk_text_buffer_finalize (GObject *object); - static void gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer, GtkTextIter *iter, const gchar *text, @@ -110,6 +146,9 @@ static void gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffe const GtkTextIter *start_char, const GtkTextIter *end_char); static void gtk_text_buffer_real_changed (GtkTextBuffer *buffer); +static void gtk_text_buffer_real_mark_set (GtkTextBuffer *buffer, + const GtkTextIter *iter, + GtkTextMark *mark); static GtkTextBTree* get_btree (GtkTextBuffer *buffer); static void free_log_attr_cache (GtkTextLogAttrCache *cache); @@ -119,7 +158,8 @@ static void update_selection_clipboards (GtkTextBuffer *buffer); static GtkTextBuffer *create_clipboard_contents_buffer (GtkTextBuffer *buffer); -static GObjectClass *parent_class = NULL; +static void gtk_text_buffer_free_target_lists (GtkTextBuffer *buffer); + static guint signals[LAST_SIGNAL] = { 0 }; static void gtk_text_buffer_set_property (GObject *object, @@ -130,45 +170,20 @@ static void gtk_text_buffer_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void gtk_text_buffer_notify (GObject *object, + GParamSpec *pspec); - -GType -gtk_text_buffer_get_type (void) -{ - static GType our_type = 0; - - if (our_type == 0) - { - static const GTypeInfo our_info = - { - sizeof (GtkTextBufferClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gtk_text_buffer_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkTextBuffer), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_text_buffer_init - }; - - our_type = g_type_register_static (G_TYPE_OBJECT, "GtkTextBuffer", - &our_info, 0); - } - - return our_type; -} +G_DEFINE_TYPE (GtkTextBuffer, gtk_text_buffer, G_TYPE_OBJECT) static void gtk_text_buffer_class_init (GtkTextBufferClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - parent_class = g_type_class_peek_parent (klass); - object_class->finalize = gtk_text_buffer_finalize; object_class->set_property = gtk_text_buffer_set_property; object_class->get_property = gtk_text_buffer_get_property; + object_class->notify = gtk_text_buffer_notify; klass->insert_text = gtk_text_buffer_real_insert_text; klass->insert_pixbuf = gtk_text_buffer_real_insert_pixbuf; @@ -177,6 +192,7 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) klass->apply_tag = gtk_text_buffer_real_apply_tag; 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; /* Construct */ g_object_class_install_property (object_class, @@ -187,8 +203,109 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) GTK_TYPE_TEXT_TAG_TABLE, GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /* Normal properties*/ + + /** + * GtkTextBuffer:text: + * + * The text content of the buffer. Without child widgets and images, + * see gtk_text_buffer_get_text() for more information. + * + * Since: 2.8 + */ + g_object_class_install_property (object_class, + PROP_TEXT, + g_param_spec_string ("text", + P_("Text"), + P_("Current text of the buffer"), + "", + GTK_PARAM_READWRITE)); + + /** + * GtkTextBuffer:has-selection: + * + * Whether the buffer has some text currently selected. + * + * Since: 2.10 + */ + g_object_class_install_property (object_class, + PROP_HAS_SELECTION, + g_param_spec_boolean ("has-selection", + P_("Has selection"), + P_("Whether the buffer has some text currently selected"), + FALSE, + GTK_PARAM_READABLE)); + + /** + * GtkTextBuffer:cursor-position: + * + * The position of the insert mark (as offset from the beginning + * of the buffer). It is useful for getting notified when the + * cursor moves. + * + * Since: 2.10 + */ + g_object_class_install_property (object_class, + PROP_CURSOR_POSITION, + g_param_spec_int ("cursor-position", + P_("Cursor position"), + P_("The position of the insert mark (as offset from the beginning of the buffer)"), + 0, G_MAXINT, 0, + GTK_PARAM_READABLE)); + + /** + * GtkTextBuffer:copy-target-list: + * + * The list of targets this buffer supports for clipboard copying + * and as DND source. + * + * Since: 2.10 + */ + g_object_class_install_property (object_class, + PROP_COPY_TARGET_LIST, + g_param_spec_boxed ("copy-target-list", + P_("Copy target list"), + P_("The list of targets this buffer supports for clipboard copying and DND source"), + GTK_TYPE_TARGET_LIST, + GTK_PARAM_READABLE)); + + /** + * GtkTextBuffer:paste-target-list: + * + * The list of targets this buffer supports for clipboard pasting + * and as DND destination. + * + * Since: 2.10 + */ + g_object_class_install_property (object_class, + PROP_PASTE_TARGET_LIST, + g_param_spec_boxed ("paste-target-list", + P_("Paste target list"), + P_("The list of targets this buffer supports for clipboard pasting and DND destination"), + GTK_TYPE_TARGET_LIST, + GTK_PARAM_READABLE)); + + /** + * GtkTextBuffer::insert-text: + * @textbuffer: the object which received the signal + * @location: position to insert @text in @textbuffer + * @text: the UTF-8 text to be inserted + * @len: length of the inserted text in bytes + * + * The ::insert-text signal is emitted to insert text in a #GtkTextBuffer. + * Insertion actually occurs in the default handler. + * + * Note that if your handler runs before the default handler it must not + * invalidate the @location iter (or has to revalidate it). + * The default signal handler revalidates it to point to the end of the + * inserted text. + * + * See also: + * gtk_text_buffer_insert(), + * gtk_text_buffer_insert_range(). + */ signals[INSERT_TEXT] = - g_signal_new ("insert_text", + g_signal_new (I_("insert-text"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, insert_text), @@ -199,9 +316,27 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE, G_TYPE_INT); + g_signal_set_va_marshaller (signals[INSERT_TEXT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_VOID__BOXED_STRING_INTv); + /** + * GtkTextBuffer::insert-pixbuf: + * @textbuffer: the object which received the signal + * @location: position to insert @pixbuf in @textbuffer + * @pixbuf: the #GdkPixbuf to be inserted + * + * The ::insert-pixbuf signal is emitted to insert a #GdkPixbuf + * in a #GtkTextBuffer. Insertion actually occurs in the default handler. + * + * Note that if your handler runs before the default handler it must not + * invalidate the @location iter (or has to revalidate it). + * The default signal handler revalidates it to be placed after the + * inserted @pixbuf. + * + * See also: gtk_text_buffer_insert_pixbuf(). + */ signals[INSERT_PIXBUF] = - g_signal_new ("insert_pixbuf", + g_signal_new (I_("insert-pixbuf"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, insert_pixbuf), @@ -212,8 +347,26 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE, GDK_TYPE_PIXBUF); + + /** + * GtkTextBuffer::insert-child-anchor: + * @textbuffer: the object which received the signal + * @location: position to insert @anchor in @textbuffer + * @anchor: the #GtkTextChildAnchor to be inserted + * + * The ::insert-child-anchor signal is emitted to insert a + * #GtkTextChildAnchor in a #GtkTextBuffer. + * Insertion actually occurs in the default handler. + * + * Note that if your handler runs before the default handler it must + * not invalidate the @location iter (or has to revalidate it). + * The default signal handler revalidates it to be placed after the + * inserted @anchor. + * + * See also: gtk_text_buffer_insert_child_anchor(). + */ signals[INSERT_CHILD_ANCHOR] = - g_signal_new ("insert_child_anchor", + g_signal_new (I_("insert-child-anchor"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, insert_child_anchor), @@ -225,20 +378,25 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) GTK_TYPE_TEXT_CHILD_ANCHOR); /** - * GtkTextBuffer::delete_range: - * @buffer: the object which received the signal. + * GtkTextBuffer::delete-range: + * @textbuffer: the object which received the signal * @start: the start of the range to be deleted * @end: the end of the range to be deleted - * - * The ::delete_range signal is emitted to delete a range from - * a #GtkTextBuffer. Note that your handler must not invalidate the - * @start and @end iters (or has to revalidate them), if it runs before the - * default handler. There is no need to keep the iters valid in handlers - * which run after the default handler (see g_signal_connect_after()), but - * those don't have access to the deleted text. + * + * The ::delete-range signal is emitted to delete a range + * from a #GtkTextBuffer. + * + * Note that if your handler runs before the default handler it must not + * invalidate the @start and @end iters (or has to revalidate them). + * The default signal handler revalidates the @start and @end iters to + * both point point to the location where text was deleted. Handlers + * which run after the default handler (see g_signal_connect_after()) + * do not have access to the deleted text. + * + * See also: gtk_text_buffer_delete(). */ signals[DELETE_RANGE] = - g_signal_new ("delete_range", + g_signal_new (I_("delete-range"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, delete_range), @@ -249,8 +407,15 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE, GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE); + /** + * GtkTextBuffer::changed: + * @textbuffer: the object which received the signal + * + * The ::changed signal is emitted when the content of a #GtkTextBuffer + * has changed. + */ signals[CHANGED] = - g_signal_new ("changed", + g_signal_new (I_("changed"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, changed), @@ -259,8 +424,18 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) G_TYPE_NONE, 0); + /** + * GtkTextBuffer::modified-changed: + * @textbuffer: the object which received the signal + * + * The ::modified-changed signal is emitted when the modified bit of a + * #GtkTextBuffer flips. + * + * See also: + * gtk_text_buffer_set_modified(). + */ signals[MODIFIED_CHANGED] = - g_signal_new ("modified_changed", + g_signal_new (I_("modified-changed"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, modified_changed), @@ -269,8 +444,21 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) G_TYPE_NONE, 0); + /** + * GtkTextBuffer::mark-set: + * @textbuffer: the object which received the signal + * @location: The location of @mark in @textbuffer + * @mark: The mark that is set + * + * The ::mark-set signal is emitted as notification + * after a #GtkTextMark is set. + * + * See also: + * gtk_text_buffer_create_mark(), + * gtk_text_buffer_move_mark(). + */ signals[MARK_SET] = - g_signal_new ("mark_set", + g_signal_new (I_("mark-set"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, mark_set), @@ -281,8 +469,19 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) GTK_TYPE_TEXT_ITER, GTK_TYPE_TEXT_MARK); + /** + * GtkTextBuffer::mark-deleted: + * @textbuffer: the object which received the signal + * @mark: The mark that was deleted + * + * The ::mark-deleted signal is emitted as notification + * after a #GtkTextMark is deleted. + * + * See also: + * gtk_text_buffer_delete_mark(). + */ signals[MARK_DELETED] = - g_signal_new ("mark_deleted", + g_signal_new (I_("mark-deleted"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, mark_deleted), @@ -291,9 +490,28 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) G_TYPE_NONE, 1, GTK_TYPE_TEXT_MARK); - + + /** + * GtkTextBuffer::apply-tag: + * @textbuffer: the object which received the signal + * @tag: the applied tag + * @start: the start of the range the tag is applied to + * @end: the end of the range the tag is applied to + * + * The ::apply-tag signal is emitted to apply a tag to a + * range of text in a #GtkTextBuffer. + * Applying actually occurs in the default handler. + * + * Note that if your handler runs before the default handler it must not + * invalidate the @start and @end iters (or has to revalidate them). + * + * See also: + * gtk_text_buffer_apply_tag(), + * gtk_text_buffer_insert_with_tags(), + * gtk_text_buffer_insert_range(). + */ signals[APPLY_TAG] = - g_signal_new ("apply_tag", + g_signal_new (I_("apply-tag"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, apply_tag), @@ -305,8 +523,26 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) GTK_TYPE_TEXT_ITER, GTK_TYPE_TEXT_ITER); + + /** + * GtkTextBuffer::remove-tag: + * @textbuffer: the object which received the signal + * @tag: the tag to be removed + * @start: the start of the range the tag is removed from + * @end: the end of the range the tag is removed from + * + * The ::remove-tag signal is emitted to remove all occurrences of @tag from + * a range of text in a #GtkTextBuffer. + * Removal actually occurs in the default handler. + * + * Note that if your handler runs before the default handler it must not + * invalidate the @start and @end iters (or has to revalidate them). + * + * See also: + * gtk_text_buffer_remove_tag(). + */ signals[REMOVE_TAG] = - g_signal_new ("remove_tag", + g_signal_new (I_("remove-tag"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, remove_tag), @@ -318,8 +554,23 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) GTK_TYPE_TEXT_ITER, GTK_TYPE_TEXT_ITER); + /** + * GtkTextBuffer::begin-user-action: + * @textbuffer: the object which received the signal + * + * The ::begin-user-action signal is emitted at the beginning of a single + * user-visible operation on a #GtkTextBuffer. + * + * See also: + * gtk_text_buffer_begin_user_action(), + * gtk_text_buffer_insert_interactive(), + * gtk_text_buffer_insert_range_interactive(), + * gtk_text_buffer_delete_interactive(), + * gtk_text_buffer_backspace(), + * gtk_text_buffer_delete_selection(). + */ signals[BEGIN_USER_ACTION] = - g_signal_new ("begin_user_action", + g_signal_new (I_("begin-user-action"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, begin_user_action), @@ -328,33 +579,81 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass) G_TYPE_NONE, 0); + /** + * GtkTextBuffer::end-user-action: + * @textbuffer: the object which received the signal + * + * The ::end-user-action signal is emitted at the end of a single + * user-visible operation on the #GtkTextBuffer. + * + * See also: + * gtk_text_buffer_end_user_action(), + * gtk_text_buffer_insert_interactive(), + * gtk_text_buffer_insert_range_interactive(), + * gtk_text_buffer_delete_interactive(), + * gtk_text_buffer_backspace(), + * gtk_text_buffer_delete_selection(), + * gtk_text_buffer_backspace(). + */ signals[END_USER_ACTION] = - g_signal_new ("end_user_action", + g_signal_new (I_("end-user-action"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTextBufferClass, end_user_action), NULL, NULL, _gtk_marshal_VOID__VOID, G_TYPE_NONE, - 0); + 0); + + /** + * GtkTextBuffer::paste-done: + * @textbuffer: the object which received the signal + * + * The paste-done signal is emitted after paste operation has been completed. + * This is useful to properly scroll the view to the end of the pasted text. + * See gtk_text_buffer_paste_clipboard() for more details. + * + * Since: 2.16 + */ + signals[PASTE_DONE] = + g_signal_new (I_("paste-done"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTextBufferClass, paste_done), + NULL, NULL, + _gtk_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + GTK_TYPE_CLIPBOARD); + + g_type_class_add_private (object_class, sizeof (GtkTextBufferPrivate)); } static void gtk_text_buffer_init (GtkTextBuffer *buffer) { - buffer->clipboard_contents_buffers = NULL; - buffer->tag_table = NULL; + buffer->priv = G_TYPE_INSTANCE_GET_PRIVATE (buffer, + GTK_TYPE_TEXT_BUFFER, + GtkTextBufferPrivate); + + buffer->priv->clipboard_contents_buffers = NULL; + buffer->priv->tag_table = NULL; + + /* allow copying of arbiatray stuff in the internal rich text format */ + gtk_text_buffer_register_serialize_tagset (buffer, NULL); } static void set_table (GtkTextBuffer *buffer, GtkTextTagTable *table) { - g_return_if_fail (buffer->tag_table == NULL); + GtkTextBufferPrivate *priv = buffer->priv; + + g_return_if_fail (priv->tag_table == NULL); if (table) { - buffer->tag_table = table; - g_object_ref (buffer->tag_table); + priv->tag_table = table; + g_object_ref (priv->tag_table); _gtk_text_tag_table_add_buffer (table, buffer); } } @@ -362,13 +661,15 @@ set_table (GtkTextBuffer *buffer, GtkTextTagTable *table) static GtkTextTagTable* get_table (GtkTextBuffer *buffer) { - if (buffer->tag_table == NULL) + GtkTextBufferPrivate *priv = buffer->priv; + + if (priv->tag_table == NULL) { - buffer->tag_table = gtk_text_tag_table_new (); - _gtk_text_tag_table_add_buffer (buffer->tag_table, buffer); + priv->tag_table = gtk_text_tag_table_new (); + _gtk_text_tag_table_add_buffer (priv->tag_table, buffer); } - return buffer->tag_table; + return priv->tag_table; } static void @@ -387,7 +688,13 @@ gtk_text_buffer_set_property (GObject *object, set_table (text_buffer, g_value_get_object (value)); break; + case PROP_TEXT: + gtk_text_buffer_set_text (text_buffer, + g_value_get_string (value), -1); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } @@ -399,6 +706,7 @@ gtk_text_buffer_get_property (GObject *object, GParamSpec *pspec) { GtkTextBuffer *text_buffer; + GtkTextIter iter; text_buffer = GTK_TEXT_BUFFER (object); @@ -408,14 +716,57 @@ gtk_text_buffer_get_property (GObject *object, g_value_set_object (value, get_table (text_buffer)); break; + case PROP_TEXT: + { + GtkTextIter start, end; + + gtk_text_buffer_get_start_iter (text_buffer, &start); + gtk_text_buffer_get_end_iter (text_buffer, &end); + + g_value_take_string (value, + gtk_text_buffer_get_text (text_buffer, + &start, &end, FALSE)); + break; + } + + case PROP_HAS_SELECTION: + g_value_set_boolean (value, text_buffer->priv->has_selection); + break; + + case PROP_CURSOR_POSITION: + gtk_text_buffer_get_iter_at_mark (text_buffer, &iter, + gtk_text_buffer_get_insert (text_buffer)); + g_value_set_int (value, gtk_text_iter_get_offset (&iter)); + break; + + case PROP_COPY_TARGET_LIST: + g_value_set_boxed (value, gtk_text_buffer_get_copy_target_list (text_buffer)); + break; + + case PROP_PASTE_TARGET_LIST: + g_value_set_boxed (value, gtk_text_buffer_get_paste_target_list (text_buffer)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static void +gtk_text_buffer_notify (GObject *object, + GParamSpec *pspec) +{ + if (!strcmp (pspec->name, "copy-target-list") || + !strcmp (pspec->name, "paste-target-list")) + { + gtk_text_buffer_free_target_lists (GTK_TEXT_BUFFER (object)); + } +} + /** * gtk_text_buffer_new: - * @table: a tag table, or NULL to create a new one + * @table: (allow-none): a tag table, or %NULL to create a new one * * Creates a new text buffer. * @@ -426,7 +777,7 @@ gtk_text_buffer_new (GtkTextTagTable *table) { GtkTextBuffer *text_buffer; - text_buffer = g_object_new (GTK_TYPE_TEXT_BUFFER, "tag_table", table, NULL); + text_buffer = g_object_new (GTK_TYPE_TEXT_BUFFER, "tag-table", table, NULL); return text_buffer; } @@ -435,40 +786,46 @@ static void gtk_text_buffer_finalize (GObject *object) { GtkTextBuffer *buffer; + GtkTextBufferPrivate *priv; buffer = GTK_TEXT_BUFFER (object); + priv = buffer->priv; remove_all_selection_clipboards (buffer); - if (buffer->tag_table) + if (priv->tag_table) { - _gtk_text_tag_table_remove_buffer (buffer->tag_table, buffer); - g_object_unref (buffer->tag_table); - buffer->tag_table = NULL; + _gtk_text_tag_table_remove_buffer (priv->tag_table, buffer); + g_object_unref (priv->tag_table); + priv->tag_table = NULL; } - if (buffer->btree) + if (priv->btree) { - _gtk_text_btree_unref (buffer->btree); - buffer->btree = NULL; + _gtk_text_btree_unref (priv->btree); + priv->btree = NULL; } - if (buffer->log_attr_cache) - free_log_attr_cache (buffer->log_attr_cache); + if (priv->log_attr_cache) + free_log_attr_cache (priv->log_attr_cache); - buffer->log_attr_cache = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); + priv->log_attr_cache = NULL; + + gtk_text_buffer_free_target_lists (buffer); + + G_OBJECT_CLASS (gtk_text_buffer_parent_class)->finalize (object); } static GtkTextBTree* get_btree (GtkTextBuffer *buffer) { - if (buffer->btree == NULL) - buffer->btree = _gtk_text_btree_new (gtk_text_buffer_get_tag_table (buffer), - buffer); + GtkTextBufferPrivate *priv = buffer->priv; - return buffer->btree; + if (priv->btree == NULL) + priv->btree = _gtk_text_btree_new (gtk_text_buffer_get_tag_table (buffer), + buffer); + + return priv->btree; } GtkTextBTree* @@ -483,10 +840,10 @@ _gtk_text_buffer_get_btree (GtkTextBuffer *buffer) * * Get the #GtkTextTagTable associated with this buffer. * - * Return value: the buffer's tag table + * Return value: (transfer none): the buffer's tag table **/ GtkTextTagTable* -gtk_text_buffer_get_tag_table (GtkTextBuffer *buffer) +gtk_text_buffer_get_tag_table (GtkTextBuffer *buffer) { g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); @@ -524,17 +881,21 @@ 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); } + + g_object_notify (G_OBJECT (buffer), "text"); } + + /* * Insertion */ static void gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer, - GtkTextIter *iter, - const gchar *text, - gint len) + GtkTextIter *iter, + const gchar *text, + gint len) { g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); g_return_if_fail (iter != NULL); @@ -542,6 +903,7 @@ gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer, _gtk_text_btree_insert (iter, text, len); g_signal_emit (buffer, signals[CHANGED], 0); + g_object_notify (G_OBJECT (buffer), "cursor-position"); } static void @@ -570,23 +932,22 @@ gtk_text_buffer_emit_insert (GtkTextBuffer *buffer, * gtk_text_buffer_insert: * @buffer: a #GtkTextBuffer * @iter: a position in the buffer - * @text: UTF-8 format text to insert + * @text: text in UTF-8 format * @len: length of text in bytes, or -1 * * Inserts @len bytes of @text at position @iter. If @len is -1, * @text must be nul-terminated and will be inserted in its - * entirety. Emits the "insert_text" signal; insertion actually occurs + * entirety. Emits the "insert-text" signal; insertion actually occurs * in the default handler for the signal. @iter is invalidated when * insertion occurs (because the buffer contents change), but the * default signal handler revalidates it to point to the end of the * inserted text. - * **/ void gtk_text_buffer_insert (GtkTextBuffer *buffer, - GtkTextIter *iter, - const gchar *text, - gint len) + GtkTextIter *iter, + const gchar *text, + gint len) { g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); g_return_if_fail (iter != NULL); @@ -599,16 +960,16 @@ gtk_text_buffer_insert (GtkTextBuffer *buffer, /** * gtk_text_buffer_insert_at_cursor: * @buffer: a #GtkTextBuffer - * @text: some text in UTF-8 format + * @text: text in UTF-8 format * @len: length of text, in bytes * - * Simply calls gtk_text_buffer_insert (), using the current + * Simply calls gtk_text_buffer_insert(), using the current * cursor position as the insertion point. **/ void gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer, - const gchar *text, - gint len) + const gchar *text, + gint len) { GtkTextIter iter; @@ -616,8 +977,7 @@ gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer, g_return_if_fail (text != NULL); gtk_text_buffer_get_iter_at_mark (buffer, &iter, - gtk_text_buffer_get_mark (buffer, - "insert")); + gtk_text_buffer_get_insert (buffer)); gtk_text_buffer_insert (buffer, &iter, text, len); } @@ -631,7 +991,7 @@ gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer, * @default_editable: default editability of buffer * * Like gtk_text_buffer_insert(), but the insertion will not occur if - * @iter is at a non-editable location in the buffer. Usually you + * @iter is at a non-editable location in the buffer. Usually you * want to prevent insertions at ineditable locations if the insertion * results from a user action (is interactive). * @@ -670,7 +1030,7 @@ gtk_text_buffer_insert_interactive (GtkTextBuffer *buffer, * @len: length of text in bytes, or -1 * @default_editable: default editability of buffer * - * Calls gtk_text_buffer_insert_interactive () at the cursor + * Calls gtk_text_buffer_insert_interactive() at the cursor * position. * * @default_editable indicates the editability of text that doesn't @@ -691,8 +1051,7 @@ gtk_text_buffer_insert_interactive_at_cursor (GtkTextBuffer *buffer, g_return_val_if_fail (text != NULL, FALSE); gtk_text_buffer_get_iter_at_mark (buffer, &iter, - gtk_text_buffer_get_mark (buffer, - "insert")); + gtk_text_buffer_get_insert (buffer)); return gtk_text_buffer_insert_interactive (buffer, &iter, text, len, default_editable); @@ -813,7 +1172,6 @@ insert_range_untagged (GtkTextBuffer *buffer, GtkTextIter range_start; GtkTextIter range_end; GtkTextIter start, end; - GtkTextBuffer *src_buffer; Range *r; if (gtk_text_iter_equal (orig_start, orig_end)) @@ -822,8 +1180,6 @@ insert_range_untagged (GtkTextBuffer *buffer, start = *orig_start; end = *orig_end; - src_buffer = gtk_text_iter_get_buffer (&start); - range_start = start; range_end = start; @@ -929,13 +1285,10 @@ insert_range_not_inside_self (GtkTextBuffer *buffer, GtkTextIter end = *orig_end; GtkTextIter range_start; GtkTextIter range_end; - GtkTextBuffer *src_buffer; if (gtk_text_iter_equal (orig_start, orig_end)) return; - src_buffer = gtk_text_iter_get_buffer (orig_start); - gtk_text_iter_order (&start, &end); range_start = start; @@ -1081,8 +1434,8 @@ gtk_text_buffer_insert_range (GtkTextBuffer *buffer, g_return_if_fail (end != NULL); g_return_if_fail (gtk_text_iter_get_buffer (start) == gtk_text_iter_get_buffer (end)); - g_return_if_fail (gtk_text_iter_get_buffer (start)->tag_table == - buffer->tag_table); + g_return_if_fail (gtk_text_iter_get_buffer (start)->priv->tag_table == + buffer->priv->tag_table); g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer); gtk_text_buffer_real_insert_range (buffer, iter, start, end, FALSE); @@ -1117,9 +1470,8 @@ gtk_text_buffer_insert_range_interactive (GtkTextBuffer *buffer, g_return_val_if_fail (end != NULL, FALSE); g_return_val_if_fail (gtk_text_iter_get_buffer (start) == gtk_text_iter_get_buffer (end), FALSE); - g_return_val_if_fail (gtk_text_iter_get_buffer (start)->tag_table == - buffer->tag_table, FALSE); - + g_return_val_if_fail (gtk_text_iter_get_buffer (start)->priv->tag_table == + buffer->priv->tag_table, FALSE); if (gtk_text_iter_can_insert (iter, default_editable)) { @@ -1137,13 +1489,13 @@ gtk_text_buffer_insert_range_interactive (GtkTextBuffer *buffer, * @text: UTF-8 text * @len: length of @text, or -1 * @first_tag: first tag to apply to @text - * @Varargs: NULL-terminated list of tags to apply + * @...: %NULL-terminated list of tags to apply * * Inserts @text into @buffer at @iter, applying the list of tags to - * the newly-inserted text. The last tag specified must be NULL to - * terminate the list. Equivalent to calling gtk_text_buffer_insert (), - * then gtk_text_buffer_apply_tag () on the inserted text; - * gtk_text_buffer_insert_with_tags () is just a convenience function. + * the newly-inserted text. The last tag specified must be %NULL to + * terminate the list. Equivalent to calling gtk_text_buffer_insert(), + * then gtk_text_buffer_apply_tag() on the inserted text; + * gtk_text_buffer_insert_with_tags() is just a convenience function. **/ void gtk_text_buffer_insert_with_tags (GtkTextBuffer *buffer, @@ -1191,9 +1543,9 @@ gtk_text_buffer_insert_with_tags (GtkTextBuffer *buffer, * @text: UTF-8 text * @len: length of @text, or -1 * @first_tag_name: name of a tag to apply to @text - * @Varargs: more tag names + * @...: more tag names * - * Same as gtk_text_buffer_insert_with_tags (), but allows you + * Same as gtk_text_buffer_insert_with_tags(), but allows you * to pass in tag names instead of tag objects. **/ void @@ -1229,12 +1581,13 @@ gtk_text_buffer_insert_with_tags_by_name (GtkTextBuffer *buffer, { GtkTextTag *tag; - tag = gtk_text_tag_table_lookup (buffer->tag_table, + tag = gtk_text_tag_table_lookup (buffer->priv->tag_table, tag_name); if (tag == NULL) { g_warning ("%s: no tag with name '%s'!", G_STRLOC, tag_name); + va_end (args); return; } @@ -1256,6 +1609,8 @@ gtk_text_buffer_real_delete_range (GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end) { + gboolean has_selection; + g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); g_return_if_fail (start != NULL); g_return_if_fail (end != NULL); @@ -1265,7 +1620,15 @@ gtk_text_buffer_real_delete_range (GtkTextBuffer *buffer, /* may have deleted the selection... */ update_selection_clipboards (buffer); + has_selection = gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL); + if (has_selection != buffer->priv->has_selection) + { + buffer->priv->has_selection = has_selection; + g_object_notify (G_OBJECT (buffer), "has-selection"); + } + g_signal_emit (buffer, signals[CHANGED], 0); + g_object_notify (G_OBJECT (buffer), "cursor-position"); } static void @@ -1295,13 +1658,12 @@ gtk_text_buffer_emit_delete (GtkTextBuffer *buffer, * @end: another position in @buffer * * Deletes text between @start and @end. The order of @start and @end - * is not actually relevant; gtk_text_buffer_delete () will reorder - * them. This function actually emits the "delete_range" signal, and + * is not actually relevant; gtk_text_buffer_delete() will reorder + * them. This function actually emits the "delete-range" signal, and * the default handler of that signal deletes the text. Because the * buffer is modified, all outstanding iterators become invalid after * calling this function; however, the @start and @end will be * re-initialized to point to the location where text was deleted. - * **/ void gtk_text_buffer_delete (GtkTextBuffer *buffer, @@ -1325,7 +1687,7 @@ gtk_text_buffer_delete (GtkTextBuffer *buffer, * @default_editable: whether the buffer is editable by default * * Deletes all editable text in the given range. - * Calls gtk_text_buffer_delete () for each editable sub-range of + * Calls gtk_text_buffer_delete() for each editable sub-range of * [@start,@end). @start and @end are revalidated to point to * the location of the last deleted range, or left untouched if * no text was deleted. @@ -1419,7 +1781,13 @@ gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer, gtk_text_buffer_emit_delete (buffer, &start, &iter); - current_state = FALSE; + /* It's more robust to ask for the state again then to assume that + * we're on the next not-editable segment. We don't know what the + * ::delete-range handler did.... maybe it deleted the following + * not-editable segment because it was associated with the editable + * segment. + */ + current_state = gtk_text_iter_editable (&iter, default_editable); deleted_stuff = TRUE; /* revalidate user's iterators. */ @@ -1434,9 +1802,7 @@ gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer, g_assert (!current_state && new_state); - gtk_text_buffer_move_mark (buffer, start_mark, - &iter); - + gtk_text_buffer_move_mark (buffer, start_mark, &iter); current_state = TRUE; } @@ -1445,7 +1811,6 @@ gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer, break; } - gtk_text_buffer_delete_mark (buffer, start_mark); gtk_text_buffer_delete_mark (buffer, end_mark); @@ -1467,19 +1832,19 @@ gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer, * * Returns the text in the range [@start,@end). Excludes undisplayed * text (text marked with tags that set the invisibility attribute) if - * @include_hidden_chars is FALSE. Does not include characters + * @include_hidden_chars is %FALSE. Does not include characters * representing embedded images, so byte and character indexes into * the returned string do not correspond to byte * and character indexes into the buffer. Contrast with - * gtk_text_buffer_get_slice (). + * gtk_text_buffer_get_slice(). * * Return value: an allocated UTF-8 string **/ gchar* -gtk_text_buffer_get_text (GtkTextBuffer *buffer, +gtk_text_buffer_get_text (GtkTextBuffer *buffer, const GtkTextIter *start, const GtkTextIter *end, - gboolean include_hidden_chars) + gboolean include_hidden_chars) { g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); g_return_val_if_fail (start != NULL, NULL); @@ -1502,22 +1867,22 @@ gtk_text_buffer_get_text (GtkTextBuffer *buffer, * * Returns the text in the range [@start,@end). Excludes undisplayed * text (text marked with tags that set the invisibility attribute) if - * @include_hidden_chars is FALSE. The returned string includes a + * @include_hidden_chars is %FALSE. The returned string includes a * 0xFFFC character whenever the buffer contains * embedded images, so byte and character indexes into * the returned string do correspond to byte * and character indexes into the buffer. Contrast with - * gtk_text_buffer_get_text (). Note that 0xFFFC can occur in normal + * gtk_text_buffer_get_text(). Note that 0xFFFC can occur in normal * text as well, so it is not a reliable indicator that a pixbuf or * widget is in the buffer. * * Return value: an allocated UTF-8 string **/ gchar* -gtk_text_buffer_get_slice (GtkTextBuffer *buffer, +gtk_text_buffer_get_slice (GtkTextBuffer *buffer, const GtkTextIter *start, const GtkTextIter *end, - gboolean include_hidden_chars) + gboolean include_hidden_chars) { g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); g_return_val_if_fail (start != NULL, NULL); @@ -1536,9 +1901,9 @@ gtk_text_buffer_get_slice (GtkTextBuffer *buffer, */ static void -gtk_text_buffer_real_insert_pixbuf (GtkTextBuffer *buffer, - GtkTextIter *iter, - GdkPixbuf *pixbuf) +gtk_text_buffer_real_insert_pixbuf (GtkTextBuffer *buffer, + GtkTextIter *iter, + GdkPixbuf *pixbuf) { _gtk_text_btree_insert_pixbuf (iter, pixbuf); @@ -1559,12 +1924,11 @@ gtk_text_buffer_real_insert_pixbuf (GtkTextBuffer *buffer, * this character for pixbufs, but the "text" variants do * not. e.g. see gtk_text_buffer_get_slice() and * gtk_text_buffer_get_text(). - * **/ void -gtk_text_buffer_insert_pixbuf (GtkTextBuffer *buffer, - GtkTextIter *iter, - GdkPixbuf *pixbuf) +gtk_text_buffer_insert_pixbuf (GtkTextBuffer *buffer, + GtkTextIter *iter, + GdkPixbuf *pixbuf) { g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); g_return_if_fail (iter != NULL); @@ -1602,12 +1966,11 @@ gtk_text_buffer_real_insert_anchor (GtkTextBuffer *buffer, * by the Unicode "object replacement character" 0xFFFC. Note that the * "slice" variants for obtaining portions of the buffer as a string * include this character for child anchors, but the "text" variants do - * not. e.g. see gtk_text_buffer_get_slice() and + * not. E.g. see gtk_text_buffer_get_slice() and * gtk_text_buffer_get_text(). Consider * gtk_text_buffer_create_child_anchor() as a more convenient * alternative to this function. The buffer will add a reference to * the anchor, so you can unref it after insertion. - * **/ void gtk_text_buffer_insert_child_anchor (GtkTextBuffer *buffer, @@ -1623,7 +1986,6 @@ gtk_text_buffer_insert_child_anchor (GtkTextBuffer *buffer, iter, anchor); } - /** * gtk_text_buffer_create_child_anchor: * @buffer: a #GtkTextBuffer @@ -1635,7 +1997,7 @@ gtk_text_buffer_insert_child_anchor (GtkTextBuffer *buffer, * owned by the buffer; no reference count is returned to * the caller of gtk_text_buffer_create_child_anchor(). * - * Return value: the created child anchor + * Return value: (transfer none): the created child anchor **/ GtkTextChildAnchor* gtk_text_buffer_create_child_anchor (GtkTextBuffer *buffer, @@ -1691,23 +2053,24 @@ gtk_text_buffer_mark_set (GtkTextBuffer *buffer, * gtk_text_buffer_set_mark: * @buffer: a #GtkTextBuffer * @mark_name: name of the mark - * @iter: location for the mark. - * @left_gravity: if the mark is created by this function, gravity for the new - * mark. + * @iter: location for the mark + * @left_gravity: if the mark is created by this function, gravity for + * the new mark * @should_exist: if %TRUE, warn if the mark does not exist, and return - * immediately. + * immediately * - * Move the mark to the given position, if not @should_exist, create the mark. + * Move the mark to the given position, if not @should_exist, + * create the mark. * * Return value: mark **/ static GtkTextMark* -gtk_text_buffer_set_mark (GtkTextBuffer *buffer, - GtkTextMark *existing_mark, - const gchar *mark_name, +gtk_text_buffer_set_mark (GtkTextBuffer *buffer, + GtkTextMark *existing_mark, + const gchar *mark_name, const GtkTextIter *iter, - gboolean left_gravity, - gboolean should_exist) + gboolean left_gravity, + gboolean should_exist) { GtkTextIter location; GtkTextMark *mark; @@ -1721,12 +2084,6 @@ gtk_text_buffer_set_mark (GtkTextBuffer *buffer, iter, should_exist); - if (_gtk_text_btree_mark_is_insert (get_btree (buffer), mark) || - _gtk_text_btree_mark_is_selection_bound (get_btree (buffer), mark)) - { - update_selection_clipboards (buffer); - } - _gtk_text_btree_get_iter_at_mark (get_btree (buffer), &location, mark); @@ -1739,13 +2096,13 @@ gtk_text_buffer_set_mark (GtkTextBuffer *buffer, /** * gtk_text_buffer_create_mark: * @buffer: a #GtkTextBuffer - * @mark_name: name for mark, or %NULL + * @mark_name: (allow-none): name for mark, or %NULL * @where: location to place mark * @left_gravity: whether the mark has left gravity * * Creates a mark at position @where. If @mark_name is %NULL, the mark * is anonymous; otherwise, the mark can be retrieved by name using - * gtk_text_buffer_get_mark (). If a mark has left gravity, and text is + * gtk_text_buffer_get_mark(). If a mark has left gravity, and text is * inserted at the mark's current location, the mark will be moved to * the left of the newly-inserted text. If the mark has right gravity * (@left_gravity = %FALSE), the mark will end up on the right of @@ -1753,21 +2110,21 @@ gtk_text_buffer_set_mark (GtkTextBuffer *buffer, * with right gravity (when you type, the cursor stays on the right * side of the text you're typing). * - * The caller of this function does not own a reference - * to the returned #GtkTextMark, so you can ignore the return value - * if you like. Marks are owned by the buffer and go away when the - * buffer does. + * The caller of this function does not own a + * reference to the returned #GtkTextMark, so you can ignore the + * return value if you like. Marks are owned by the buffer and go + * away when the buffer does. * - * Emits the "mark_set" signal as notification of the mark's initial + * Emits the "mark-set" signal as notification of the mark's initial * placement. * - * Return value: the new #GtkTextMark object + * Return value: (transfer none): the new #GtkTextMark object **/ GtkTextMark* -gtk_text_buffer_create_mark (GtkTextBuffer *buffer, - const gchar *mark_name, +gtk_text_buffer_create_mark (GtkTextBuffer *buffer, + const gchar *mark_name, const GtkTextIter *where, - gboolean left_gravity) + gboolean left_gravity) { g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); @@ -1775,18 +2132,56 @@ gtk_text_buffer_create_mark (GtkTextBuffer *buffer, left_gravity, FALSE); } +/** + * gtk_text_buffer_add_mark: + * @buffer: a #GtkTextBuffer + * @mark: the mark to add + * @where: location to place mark + * + * Adds the mark at position @where. The mark must not be added to + * another buffer, and if its name is not %NULL then there must not + * be another mark in the buffer with the same name. + * + * Emits the "mark-set" signal as notification of the mark's initial + * placement. + * + * Since: 2.12 + **/ +void +gtk_text_buffer_add_mark (GtkTextBuffer *buffer, + GtkTextMark *mark, + const GtkTextIter *where) +{ + const gchar *name; + + g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); + g_return_if_fail (GTK_IS_TEXT_MARK (mark)); + g_return_if_fail (where != NULL); + g_return_if_fail (gtk_text_mark_get_buffer (mark) == NULL); + + name = gtk_text_mark_get_name (mark); + + if (name != NULL && gtk_text_buffer_get_mark (buffer, name) != NULL) + { + g_critical ("Mark %s already exists in the buffer", name); + return; + } + + gtk_text_buffer_set_mark (buffer, mark, NULL, where, FALSE, FALSE); +} + /** * gtk_text_buffer_move_mark: * @buffer: a #GtkTextBuffer * @mark: a #GtkTextMark * @where: new location for @mark in @buffer * - * Moves @mark to the new location @where. Emits the "mark_set" signal + * Moves @mark to the new location @where. Emits the "mark-set" signal * as notification of the move. **/ void -gtk_text_buffer_move_mark (GtkTextBuffer *buffer, - GtkTextMark *mark, +gtk_text_buffer_move_mark (GtkTextBuffer *buffer, + GtkTextMark *mark, const GtkTextIter *where) { g_return_if_fail (GTK_IS_TEXT_MARK (mark)); @@ -1799,15 +2194,15 @@ gtk_text_buffer_move_mark (GtkTextBuffer *buffer, /** * gtk_text_buffer_get_iter_at_mark: * @buffer: a #GtkTextBuffer - * @iter: iterator to initialize + * @iter: (out): iterator to initialize * @mark: a #GtkTextMark in @buffer * * Initializes @iter with the current position of @mark. **/ void gtk_text_buffer_get_iter_at_mark (GtkTextBuffer *buffer, - GtkTextIter *iter, - GtkTextMark *mark) + GtkTextIter *iter, + GtkTextMark *mark) { g_return_if_fail (GTK_IS_TEXT_MARK (mark)); g_return_if_fail (!gtk_text_mark_get_deleted (mark)); @@ -1825,13 +2220,13 @@ gtk_text_buffer_get_iter_at_mark (GtkTextBuffer *buffer, * * Deletes @mark, so that it's no longer located anywhere in the * buffer. Removes the reference the buffer holds to the mark, so if - * you haven't called g_object_ref () on the mark, it will be freed. Even + * you haven't called g_object_ref() on the mark, it will be freed. Even * if the mark isn't freed, most operations on @mark become - * invalid. There is no way to undelete a - * mark. gtk_text_mark_get_deleted () will return TRUE after this - * function has been called on a mark; gtk_text_mark_get_deleted () - * indicates that a mark no longer belongs to a buffer. The "mark_deleted" - * signal will be emitted as notification after the mark is deleted. + * invalid, until it gets added to a buffer again with + * gtk_text_buffer_add_mark(). Use gtk_text_mark_get_deleted() to + * find out if a mark has been removed from its buffer. + * The "mark-deleted" signal will be emitted as notification after + * the mark is deleted. **/ void gtk_text_buffer_delete_mark (GtkTextBuffer *buffer, @@ -1861,14 +2256,14 @@ gtk_text_buffer_delete_mark (GtkTextBuffer *buffer, * @buffer: a #GtkTextBuffer * @name: a mark name * - * Returns the mark named @name in buffer @buffer, or NULL if no such + * Returns the mark named @name in buffer @buffer, or %NULL if no such * mark exists in the buffer. * - * Return value: a #GtkTextMark, or NULL + * Return value: (transfer none): a #GtkTextMark, or %NULL **/ GtkTextMark* -gtk_text_buffer_get_mark (GtkTextBuffer *buffer, - const gchar *name) +gtk_text_buffer_get_mark (GtkTextBuffer *buffer, + const gchar *name) { GtkTextMark *mark; @@ -1880,7 +2275,6 @@ gtk_text_buffer_get_mark (GtkTextBuffer *buffer, return mark; } - /** * gtk_text_buffer_move_mark_by_name: * @buffer: a #GtkTextBuffer @@ -1888,7 +2282,7 @@ gtk_text_buffer_get_mark (GtkTextBuffer *buffer, * @where: new location for mark * * Moves the mark named @name (which must exist) to location @where. - * See gtk_text_buffer_move_mark () for details. + * See gtk_text_buffer_move_mark() for details. **/ void gtk_text_buffer_move_mark_by_name (GtkTextBuffer *buffer, @@ -1917,11 +2311,11 @@ gtk_text_buffer_move_mark_by_name (GtkTextBuffer *buffer, * @name: name of a mark in @buffer * * Deletes the mark named @name; the mark must exist. See - * gtk_text_buffer_delete_mark () for details. + * gtk_text_buffer_delete_mark() for details. **/ void -gtk_text_buffer_delete_mark_by_name (GtkTextBuffer *buffer, - const gchar *name) +gtk_text_buffer_delete_mark_by_name (GtkTextBuffer *buffer, + const gchar *name) { GtkTextMark *mark; @@ -1944,19 +2338,18 @@ gtk_text_buffer_delete_mark_by_name (GtkTextBuffer *buffer, * @buffer: a #GtkTextBuffer * * Returns the mark that represents the cursor (insertion point). - * Equivalent to calling gtk_text_buffer_get_mark () to get the mark + * Equivalent to calling gtk_text_buffer_get_mark() to get the mark * named "insert", but very slightly more efficient, and involves less * typing. * - * Return value: insertion point mark + * Return value: (transfer none): insertion point mark **/ GtkTextMark* gtk_text_buffer_get_insert (GtkTextBuffer *buffer) { g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); - /* FIXME use struct member in btree */ - return gtk_text_buffer_get_mark (buffer, "insert"); + return _gtk_text_btree_get_insert (get_btree (buffer)); } /** @@ -1964,7 +2357,7 @@ gtk_text_buffer_get_insert (GtkTextBuffer *buffer) * @buffer: a #GtkTextBuffer * * Returns the mark that represents the selection bound. Equivalent - * to calling gtk_text_buffer_get_mark () to get the mark named + * to calling gtk_text_buffer_get_mark() to get the mark named * "selection_bound", but very slightly more efficient, and involves * less typing. * @@ -1975,25 +2368,23 @@ gtk_text_buffer_get_insert (GtkTextBuffer *buffer) * for handling the selection, if you just want to know whether there's a * selection and what its bounds are. * - * Return value: selection bound mark + * Return value: (transfer none): selection bound mark **/ GtkTextMark* gtk_text_buffer_get_selection_bound (GtkTextBuffer *buffer) { g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); - /* FIXME use struct member in btree */ - return gtk_text_buffer_get_mark (buffer, "selection_bound"); + return _gtk_text_btree_get_selection_bound (get_btree (buffer)); } /** * gtk_text_buffer_get_iter_at_child_anchor: * @buffer: a #GtkTextBuffer - * @iter: an iterator to be initialized + * @iter: (out): an iterator to be initialized * @anchor: a child anchor that appears in @buffer * * Obtains the location of @anchor within @buffer. - * **/ void gtk_text_buffer_get_iter_at_child_anchor (GtkTextBuffer *buffer, @@ -2030,7 +2421,6 @@ gtk_text_buffer_place_cursor (GtkTextBuffer *buffer, gtk_text_buffer_select_range (buffer, where, where); } - /** * gtk_text_buffer_select_range: * @buffer: a #GtkTextBuffer @@ -2062,12 +2452,9 @@ gtk_text_buffer_select_range (GtkTextBuffer *buffer, _gtk_text_btree_select_range (get_btree (buffer), &real_ins, &real_bound); gtk_text_buffer_mark_set (buffer, &real_ins, - gtk_text_buffer_get_mark (buffer, - "insert")); + gtk_text_buffer_get_insert (buffer)); gtk_text_buffer_mark_set (buffer, &real_bound, - gtk_text_buffer_get_mark (buffer, - "selection_bound")); - update_selection_clipboards (buffer); + gtk_text_buffer_get_selection_bound (buffer)); } /* @@ -2077,13 +2464,12 @@ gtk_text_buffer_select_range (GtkTextBuffer *buffer, /** * gtk_text_buffer_create_tag: * @buffer: a #GtkTextBuffer - * @tag_name: name of the new tag, or %NULL - * @first_property_name: name of first property to set, or %NULL - * @Varargs: %NULL-terminated list of property names and values - * + * @tag_name: (allow-none): name of the new tag, or %NULL + * @first_property_name: (allow-none): name of first property to set, or %NULL + * @...: %NULL-terminated list of property names and values * * Creates a tag and adds it to the tag table for @buffer. - * Equivalent to calling gtk_text_tag_new () and then adding the + * Equivalent to calling gtk_text_tag_new() and then adding the * tag to the buffer's tag table. The returned tag is owned by * the buffer's tag table, so the ref count will be equal to one. * @@ -2095,8 +2481,8 @@ gtk_text_buffer_select_range (GtkTextBuffer *buffer, * The @first_property_name argument and subsequent arguments are a list * of properties to set on the tag, as with g_object_set(). * - * Return value: a new tag - **/ + * Return value: (transfer none): a new tag + */ GtkTextTag* gtk_text_buffer_create_tag (GtkTextBuffer *buffer, const gchar *tag_name, @@ -2125,12 +2511,12 @@ gtk_text_buffer_create_tag (GtkTextBuffer *buffer, } static void -gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer, - GtkTextTag *tag, +gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer, + GtkTextTag *tag, const GtkTextIter *start, const GtkTextIter *end) { - if (tag->table != buffer->tag_table) + if (tag->priv->table != buffer->priv->tag_table) { g_warning ("Can only apply tags that are in the tag table for the buffer"); return; @@ -2140,12 +2526,12 @@ gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer, } static void -gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer, - GtkTextTag *tag, +gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer, + GtkTextTag *tag, const GtkTextIter *start, const GtkTextIter *end) { - if (tag->table != buffer->tag_table) + if (tag->priv->table != buffer->priv->tag_table) { g_warning ("Can only remove tags that are in the tag table for the buffer"); return; @@ -2161,9 +2547,39 @@ gtk_text_buffer_real_changed (GtkTextBuffer *buffer) } static void -gtk_text_buffer_emit_tag (GtkTextBuffer *buffer, - GtkTextTag *tag, - gboolean apply, +gtk_text_buffer_real_mark_set (GtkTextBuffer *buffer, + const GtkTextIter *iter, + GtkTextMark *mark) +{ + GtkTextMark *insert; + + insert = gtk_text_buffer_get_insert (buffer); + + if (mark == insert || mark == gtk_text_buffer_get_selection_bound (buffer)) + { + gboolean has_selection; + + update_selection_clipboards (buffer); + + has_selection = gtk_text_buffer_get_selection_bounds (buffer, + NULL, + NULL); + + if (has_selection != buffer->priv->has_selection) + { + buffer->priv->has_selection = has_selection; + g_object_notify (G_OBJECT (buffer), "has-selection"); + } + } + + if (mark == insert) + g_object_notify (G_OBJECT (buffer), "cursor-position"); +} + +static void +gtk_text_buffer_emit_tag (GtkTextBuffer *buffer, + GtkTextTag *tag, + gboolean apply, const GtkTextIter *start, const GtkTextIter *end) { @@ -2184,7 +2600,6 @@ gtk_text_buffer_emit_tag (GtkTextBuffer *buffer, tag, &start_tmp, &end_tmp); } - /** * gtk_text_buffer_apply_tag: * @buffer: a #GtkTextBuffer @@ -2192,14 +2607,13 @@ gtk_text_buffer_emit_tag (GtkTextBuffer *buffer, * @start: one bound of range to be tagged * @end: other bound of range to be tagged * - * Emits the "apply_tag" signal on @buffer. The default + * Emits the "apply-tag" signal on @buffer. The default * handler for the signal applies @tag to the given range. * @start and @end do not have to be in order. - * **/ void -gtk_text_buffer_apply_tag (GtkTextBuffer *buffer, - GtkTextTag *tag, +gtk_text_buffer_apply_tag (GtkTextBuffer *buffer, + GtkTextTag *tag, const GtkTextIter *start, const GtkTextIter *end) { @@ -2209,7 +2623,7 @@ gtk_text_buffer_apply_tag (GtkTextBuffer *buffer, g_return_if_fail (end != NULL); g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer); g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer); - g_return_if_fail (tag->table == buffer->tag_table); + g_return_if_fail (tag->priv->table == buffer->priv->tag_table); gtk_text_buffer_emit_tag (buffer, tag, TRUE, start, end); } @@ -2221,14 +2635,13 @@ gtk_text_buffer_apply_tag (GtkTextBuffer *buffer, * @start: one bound of range to be untagged * @end: other bound of range to be untagged * - * Emits the "remove_tag" signal. The default handler for the signal + * Emits the "remove-tag" signal. The default handler for the signal * removes all occurrences of @tag from the given range. @start and * @end don't have to be in order. - * **/ void -gtk_text_buffer_remove_tag (GtkTextBuffer *buffer, - GtkTextTag *tag, +gtk_text_buffer_remove_tag (GtkTextBuffer *buffer, + GtkTextTag *tag, const GtkTextIter *start, const GtkTextIter *end) @@ -2239,12 +2652,11 @@ gtk_text_buffer_remove_tag (GtkTextBuffer *buffer, g_return_if_fail (end != NULL); g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer); g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer); - g_return_if_fail (tag->table == buffer->tag_table); + g_return_if_fail (tag->priv->table == buffer->priv->tag_table); gtk_text_buffer_emit_tag (buffer, tag, FALSE, start, end); } - /** * gtk_text_buffer_apply_tag_by_name: * @buffer: a #GtkTextBuffer @@ -2254,11 +2666,10 @@ gtk_text_buffer_remove_tag (GtkTextBuffer *buffer, * * Calls gtk_text_tag_table_lookup() on the buffer's tag table to * get a #GtkTextTag, then calls gtk_text_buffer_apply_tag(). - * **/ void -gtk_text_buffer_apply_tag_by_name (GtkTextBuffer *buffer, - const gchar *name, +gtk_text_buffer_apply_tag_by_name (GtkTextBuffer *buffer, + const gchar *name, const GtkTextIter *start, const GtkTextIter *end) { @@ -2292,12 +2703,10 @@ gtk_text_buffer_apply_tag_by_name (GtkTextBuffer *buffer, * * Calls gtk_text_tag_table_lookup() on the buffer's tag table to * get a #GtkTextTag, then calls gtk_text_buffer_remove_tag(). - * - * **/ void -gtk_text_buffer_remove_tag_by_name (GtkTextBuffer *buffer, - const gchar *name, +gtk_text_buffer_remove_tag_by_name (GtkTextBuffer *buffer, + const gchar *name, const GtkTextIter *start, const GtkTextIter *end) { @@ -2354,7 +2763,7 @@ gtk_text_buffer_remove_all_tags (GtkTextBuffer *buffer, GtkTextIter first, second, tmp; GSList *tags; GSList *tmp_list; - GSList *prev; + GSList *prev, *next; GtkTextTag *tag; g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); @@ -2409,14 +2818,15 @@ gtk_text_buffer_remove_all_tags (GtkTextBuffer *buffer, if (tag == tmp_list->data) { /* duplicate */ + next = tmp_list->next; if (prev) - prev->next = tmp_list->next; + prev->next = next; tmp_list->next = NULL; g_slist_free (tmp_list); - tmp_list = prev->next; + tmp_list = next; /* prev is unchanged */ } else @@ -2453,7 +2863,7 @@ gtk_text_buffer_remove_all_tags (GtkTextBuffer *buffer, /** * gtk_text_buffer_get_iter_at_line_offset: * @buffer: a #GtkTextBuffer - * @iter: iterator to initialize + * @iter: (out): iterator to initialize * @line_number: line number counting from 0 * @char_offset: char offset from start of line * @@ -2461,13 +2871,12 @@ gtk_text_buffer_remove_all_tags (GtkTextBuffer *buffer, * line. The @char_offset must exist, offsets off the end of the line * are not allowed. Note characters, not bytes; * UTF-8 may encode one character as multiple bytes. - * **/ void -gtk_text_buffer_get_iter_at_line_offset (GtkTextBuffer *buffer, - GtkTextIter *iter, - gint line_number, - gint char_offset) +gtk_text_buffer_get_iter_at_line_offset (GtkTextBuffer *buffer, + GtkTextIter *iter, + gint line_number, + gint char_offset) { g_return_if_fail (iter != NULL); g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); @@ -2479,7 +2888,7 @@ gtk_text_buffer_get_iter_at_line_offset (GtkTextBuffer *buffer, /** * gtk_text_buffer_get_iter_at_line_index: * @buffer: a #GtkTextBuffer - * @iter: iterator to initialize + * @iter: (out): iterator to initialize * @line_number: line number counting from 0 * @byte_index: byte index from start of line * @@ -2487,7 +2896,6 @@ gtk_text_buffer_get_iter_at_line_offset (GtkTextBuffer *buffer, * @byte_index must be the start of a UTF-8 character, and must not be * beyond the end of the line. Note bytes, not * characters; UTF-8 may encode one character as multiple bytes. - * **/ void gtk_text_buffer_get_iter_at_line_index (GtkTextBuffer *buffer, @@ -2505,15 +2913,15 @@ gtk_text_buffer_get_iter_at_line_index (GtkTextBuffer *buffer, /** * gtk_text_buffer_get_iter_at_line: * @buffer: a #GtkTextBuffer - * @iter: iterator to initialize + * @iter: (out): iterator to initialize * @line_number: line number counting from 0 * * Initializes @iter to the start of the given line. **/ void -gtk_text_buffer_get_iter_at_line (GtkTextBuffer *buffer, - GtkTextIter *iter, - gint line_number) +gtk_text_buffer_get_iter_at_line (GtkTextBuffer *buffer, + GtkTextIter *iter, + gint line_number) { g_return_if_fail (iter != NULL); g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); @@ -2524,7 +2932,7 @@ gtk_text_buffer_get_iter_at_line (GtkTextBuffer *buffer, /** * gtk_text_buffer_get_iter_at_offset: * @buffer: a #GtkTextBuffer - * @iter: iterator to initialize + * @iter: (out): iterator to initialize * @char_offset: char offset from start of buffer, counting from 0, or -1 * * Initializes @iter to a position @char_offset chars from the start @@ -2533,9 +2941,9 @@ gtk_text_buffer_get_iter_at_line (GtkTextBuffer *buffer, * the iterator one past the last valid character in the buffer. **/ void -gtk_text_buffer_get_iter_at_offset (GtkTextBuffer *buffer, - GtkTextIter *iter, - gint char_offset) +gtk_text_buffer_get_iter_at_offset (GtkTextBuffer *buffer, + GtkTextIter *iter, + gint char_offset) { g_return_if_fail (iter != NULL); g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); @@ -2546,7 +2954,7 @@ gtk_text_buffer_get_iter_at_offset (GtkTextBuffer *buffer, /** * gtk_text_buffer_get_start_iter: * @buffer: a #GtkTextBuffer - * @iter: iterator to initialize + * @iter: (out): iterator to initialize * * Initialized @iter with the first position in the text buffer. This * is the same as using gtk_text_buffer_get_iter_at_offset() to get @@ -2565,7 +2973,7 @@ gtk_text_buffer_get_start_iter (GtkTextBuffer *buffer, /** * gtk_text_buffer_get_end_iter: * @buffer: a #GtkTextBuffer - * @iter: iterator to initialize + * @iter: (out): iterator to initialize * * Initializes @iter with the "end iterator," one past the last valid * character in the text buffer. If dereferenced with @@ -2573,11 +2981,10 @@ gtk_text_buffer_get_start_iter (GtkTextBuffer *buffer, * 0. The entire buffer lies in the range from the first position in * the buffer (call gtk_text_buffer_get_start_iter() to get * character position 0) to the end iterator. - * **/ void -gtk_text_buffer_get_end_iter (GtkTextBuffer *buffer, - GtkTextIter *iter) +gtk_text_buffer_get_end_iter (GtkTextBuffer *buffer, + GtkTextIter *iter) { g_return_if_fail (iter != NULL); g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); @@ -2588,12 +2995,11 @@ gtk_text_buffer_get_end_iter (GtkTextBuffer *buffer, /** * gtk_text_buffer_get_bounds: * @buffer: a #GtkTextBuffer - * @start: iterator to initialize with first position in the buffer - * @end: iterator to initialize with the end iterator + * @start: (out): iterator to initialize with first position in the buffer + * @end: (out): iterator to initialize with the end iterator * * Retrieves the first and last iterators in the buffer, i.e. the * entire buffer lies within the range [@start,@end). - * **/ void gtk_text_buffer_get_bounds (GtkTextBuffer *buffer, @@ -2628,7 +3034,7 @@ gtk_text_buffer_get_modified (GtkTextBuffer *buffer) { g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE); - return buffer->modified; + return buffer->priv->modified; } /** @@ -2640,12 +3046,11 @@ gtk_text_buffer_get_modified (GtkTextBuffer *buffer) * last time it was saved. Whenever the buffer is saved to disk, call * gtk_text_buffer_set_modified (@buffer, FALSE). When the buffer is modified, * it will automatically toggled on the modified bit again. When the modified - * bit flips, the buffer emits a "modified_changed" signal. - * + * bit flips, the buffer emits a "modified-changed" signal. **/ void -gtk_text_buffer_set_modified (GtkTextBuffer *buffer, - gboolean setting) +gtk_text_buffer_set_modified (GtkTextBuffer *buffer, + gboolean setting) { gboolean fixed_setting; @@ -2653,25 +3058,43 @@ gtk_text_buffer_set_modified (GtkTextBuffer *buffer, fixed_setting = setting != FALSE; - if (buffer->modified == fixed_setting) + if (buffer->priv->modified == fixed_setting) return; else { - buffer->modified = fixed_setting; + buffer->priv->modified = fixed_setting; g_signal_emit (buffer, signals[MODIFIED_CHANGED], 0); } } - -/* - * Assorted other stuff - */ - /** - * gtk_text_buffer_get_line_count: + * gtk_text_buffer_get_has_selection: * @buffer: a #GtkTextBuffer * - * Obtains the number of lines in the buffer. This value is cached, so + * Indicates whether the buffer has some text currently selected. + * + * Return value: %TRUE if the there is text selected + * + * Since: 2.10 + **/ +gboolean +gtk_text_buffer_get_has_selection (GtkTextBuffer *buffer) +{ + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE); + + return buffer->priv->has_selection; +} + + +/* + * Assorted other stuff + */ + +/** + * gtk_text_buffer_get_line_count: + * @buffer: a #GtkTextBuffer + * + * Obtains the number of lines in the buffer. This value is cached, so * the function is very fast. * * Return value: number of lines in the buffer @@ -2715,13 +3138,13 @@ clipboard_clear_selection_cb (GtkClipboard *clipboard, GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data); gtk_text_buffer_get_iter_at_mark (buffer, &insert, - gtk_text_buffer_get_mark (buffer, "insert")); + gtk_text_buffer_get_insert (buffer)); gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound, - gtk_text_buffer_get_mark (buffer, "selection_bound")); + gtk_text_buffer_get_selection_bound (buffer)); if (!gtk_text_iter_equal (&insert, &selection_bound)) gtk_text_buffer_move_mark (buffer, - gtk_text_buffer_get_mark (buffer, "selection_bound"), + gtk_text_buffer_get_selection_bound (buffer), &insert); } @@ -2739,22 +3162,36 @@ clipboard_get_selection_cb (GtkClipboard *clipboard, if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end)) { - if (selection_data->target == - gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE)) + if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS) { /* Provide the address of the buffer; this will only be * used within-process */ gtk_selection_data_set (selection_data, - gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE), + gtk_selection_data_get_target (selection_data), 8, /* bytes */ (void*)&buffer, sizeof (buffer)); } + else if (info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT) + { + guint8 *str; + gsize len; + + str = gtk_text_buffer_serialize (buffer, buffer, + gtk_selection_data_get_target (selection_data), + &start, &end, &len); + + gtk_selection_data_set (selection_data, + gtk_selection_data_get_target (selection_data), + 8, /* bytes */ + str, len); + g_free (str); + } else { gchar *str; - + str = gtk_text_iter_get_visible_text (&start, &end); gtk_selection_data_set_text (selection_data, str, -1); g_free (str); @@ -2769,8 +3206,18 @@ create_clipboard_contents_buffer (GtkTextBuffer *buffer) contents = gtk_text_buffer_new (gtk_text_buffer_get_tag_table (buffer)); - g_object_set_data (G_OBJECT (contents), "gtk-text-buffer-clipboard", GINT_TO_POINTER (1)); - + g_object_set_data (G_OBJECT (contents), I_("gtk-text-buffer-clipboard-source"), + buffer); + g_object_set_data (G_OBJECT (contents), I_("gtk-text-buffer-clipboard"), + GINT_TO_POINTER (1)); + + /* Ref the source buffer as long as the clipboard contents buffer + * exists, because it's needed for serializing the contents buffer. + * See http://bugzilla.gnome.org/show_bug.cgi?id=339195 + */ + g_object_ref (buffer); + g_object_weak_ref (G_OBJECT (contents), (GWeakNotify) g_object_unref, buffer); + return contents; } @@ -2781,31 +3228,50 @@ clipboard_get_contents_cb (GtkClipboard *clipboard, guint info, gpointer data) { - GtkTextBuffer *contents; + GtkTextBuffer *contents = GTK_TEXT_BUFFER (data); - contents = GTK_TEXT_BUFFER (data); - g_assert (contents); /* This should never be called unless we own the clipboard */ - if (selection_data->target == - gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE)) + if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS) { /* Provide the address of the clipboard buffer; this will only * be used within-process. OK to supply a NULL value for contents. */ gtk_selection_data_set (selection_data, - gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE), + gtk_selection_data_get_target (selection_data), 8, /* bytes */ (void*)&contents, sizeof (contents)); } + else if (info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT) + { + GtkTextBuffer *clipboard_source_buffer; + GtkTextIter start, end; + guint8 *str; + gsize len; + + clipboard_source_buffer = g_object_get_data (G_OBJECT (contents), + "gtk-text-buffer-clipboard-source"); + + gtk_text_buffer_get_bounds (contents, &start, &end); + + str = gtk_text_buffer_serialize (clipboard_source_buffer, contents, + gtk_selection_data_get_target (selection_data), + &start, &end, &len); + + gtk_selection_data_set (selection_data, + gtk_selection_data_get_target (selection_data), + 8, /* bytes */ + str, len); + g_free (str); + } else { gchar *str; GtkTextIter start, end; - + gtk_text_buffer_get_bounds (contents, &start, &end); - + str = gtk_text_iter_get_visible_text (&start, &end); gtk_selection_data_set_text (selection_data, str, -1); g_free (str); @@ -2837,15 +3303,12 @@ get_paste_point (GtkTextBuffer *buffer, gtk_text_buffer_get_iter_at_mark (buffer, &insert_point, paste_point_override); if (clear_afterward) - gtk_text_buffer_delete_mark (buffer, - gtk_text_buffer_get_mark (buffer, - "gtk_paste_point_override")); + gtk_text_buffer_delete_mark (buffer, paste_point_override); } else { gtk_text_buffer_get_iter_at_mark (buffer, &insert_point, - gtk_text_buffer_get_mark (buffer, - "insert")); + gtk_text_buffer_get_insert (buffer)); } *iter = insert_point; @@ -2892,6 +3355,20 @@ post_paste_cleanup (ClipboardRequest *request_data) } } +static void +emit_paste_done (GtkTextBuffer *buffer, + GtkClipboard *clipboard) +{ + g_signal_emit (buffer, signals[PASTE_DONE], 0, clipboard); +} + +static void +free_clipboard_request (ClipboardRequest *request_data) +{ + g_object_unref (request_data->buffer); + g_free (request_data); +} + /* Called when we request a paste and receive the text data */ static void @@ -2922,10 +3399,23 @@ clipboard_text_received (GtkClipboard *clipboard, if (request_data->interactive) gtk_text_buffer_end_user_action (buffer); + + emit_paste_done (buffer, clipboard); } + else + { + /* It may happen that we set a point override but we are not inserting + any text, so we must remove it afterwards */ + GtkTextMark *paste_point_override; - g_object_unref (buffer); - g_free (request_data); + paste_point_override = gtk_text_buffer_get_mark (buffer, + "gtk_paste_point_override"); + + if (paste_point_override != NULL) + gtk_text_buffer_delete_mark (buffer, paste_point_override); + } + + free_clipboard_request (request_data); } static GtkTextBuffer* @@ -2936,22 +3426,22 @@ selection_data_get_buffer (GtkSelectionData *selection_data, GtkTextBuffer *src_buffer = NULL; /* If we can get the owner, the selection is in-process */ - owner = gdk_selection_owner_get_for_display (selection_data->display, - selection_data->selection); + owner = gdk_selection_owner_get_for_display (gtk_selection_data_get_display (selection_data), + gtk_selection_data_get_selection (selection_data)); if (owner == NULL) return NULL; if (gdk_window_get_window_type (owner) == GDK_WINDOW_FOREIGN) return NULL; - - if (selection_data->type != gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE)) + + if (gtk_selection_data_get_data_type (selection_data) != gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS")) return NULL; - if (selection_data->length != sizeof (src_buffer)) + if (gtk_selection_data_get_length (selection_data) != sizeof (src_buffer)) return NULL; - - memcpy (&src_buffer, selection_data->data, sizeof (src_buffer)); + + memcpy (&src_buffer, gtk_selection_data_get_data (selection_data), sizeof (src_buffer)); if (src_buffer == NULL) return NULL; @@ -2993,10 +3483,66 @@ restore_iter (const GtkTextIter *iter, #endif static void -paste_from_buffer (ClipboardRequest *request_data, - GtkTextBuffer *src_buffer, - const GtkTextIter *start, - const GtkTextIter *end) +clipboard_rich_text_received (GtkClipboard *clipboard, + GdkAtom format, + const guint8 *text, + gsize length, + gpointer data) +{ + ClipboardRequest *request_data = data; + GtkTextIter insert_point; + gboolean retval = TRUE; + GError *error = NULL; + + if (text != NULL && length > 0) + { + pre_paste_prep (request_data, &insert_point); + + if (request_data->interactive) + gtk_text_buffer_begin_user_action (request_data->buffer); + + if (!request_data->interactive || + gtk_text_iter_can_insert (&insert_point, + request_data->default_editable)) + { + retval = gtk_text_buffer_deserialize (request_data->buffer, + request_data->buffer, + format, + &insert_point, + text, length, + &error); + } + + if (!retval) + { + g_warning ("error pasting: %s\n", error->message); + g_clear_error (&error); + } + + if (request_data->interactive) + gtk_text_buffer_end_user_action (request_data->buffer); + + emit_paste_done (request_data->buffer, clipboard); + + if (retval) + { + post_paste_cleanup (request_data); + return; + } + } + + /* Request the text selection instead */ + gtk_clipboard_request_text (clipboard, + clipboard_text_received, + data); +} + +static void +paste_from_buffer (GtkClipboard *clipboard, + ClipboardRequest *request_data, + GtkTextBuffer *src_buffer, + const GtkTextIter *start, + const GtkTextIter *end) { GtkTextIter insert_point; GtkTextBuffer *buffer = request_data->buffer; @@ -3026,8 +3572,11 @@ paste_from_buffer (ClipboardRequest *request_data, if (request_data->interactive) gtk_text_buffer_end_user_action (buffer); + emit_paste_done (buffer, clipboard); + g_object_unref (src_buffer); - g_free (request_data); + + free_clipboard_request (request_data); } static void @@ -3037,7 +3586,7 @@ clipboard_clipboard_buffer_received (GtkClipboard *clipboard, { ClipboardRequest *request_data = data; GtkTextBuffer *src_buffer; - + src_buffer = selection_data_get_buffer (selection_data, request_data); if (src_buffer) @@ -3047,34 +3596,38 @@ clipboard_clipboard_buffer_received (GtkClipboard *clipboard, if (g_object_get_data (G_OBJECT (src_buffer), "gtk-text-buffer-clipboard")) { gtk_text_buffer_get_bounds (src_buffer, &start, &end); - - paste_from_buffer (request_data, src_buffer, + + paste_from_buffer (clipboard, request_data, src_buffer, &start, &end); } else { if (gtk_text_buffer_get_selection_bounds (src_buffer, &start, &end)) - paste_from_buffer (request_data, src_buffer, + paste_from_buffer (clipboard, request_data, src_buffer, &start, &end); } } else { - /* Request the text selection instead */ - gtk_clipboard_request_text (clipboard, - clipboard_text_received, - data); + if (gtk_clipboard_wait_is_rich_text_available (clipboard, + request_data->buffer)) + { + /* Request rich text */ + gtk_clipboard_request_rich_text (clipboard, + request_data->buffer, + clipboard_rich_text_received, + data); + } + else + { + /* Request the text selection instead */ + gtk_clipboard_request_text (clipboard, + clipboard_text_received, + data); + } } } -static const GtkTargetEntry targets[] = { - { "STRING", 0, TARGET_STRING }, - { "TEXT", 0, TARGET_TEXT }, - { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }, - { "UTF8_STRING", 0, TARGET_UTF8_STRING }, - { "GTK_TEXT_BUFFER_CONTENTS", 0, TARGET_TEXT_BUFFER_CONTENTS } -}; - typedef struct { GtkClipboard *clipboard; @@ -3084,7 +3637,13 @@ typedef struct static void update_selection_clipboards (GtkTextBuffer *buffer) { - GSList *tmp_list = buffer->selection_clipboards; + GtkTextBufferPrivate *priv; + GSList *tmp_list = buffer->priv->selection_clipboards; + + priv = buffer->priv; + + gtk_text_buffer_get_copy_target_list (buffer); + while (tmp_list) { GtkTextIter start; @@ -3106,7 +3665,9 @@ update_selection_clipboards (GtkTextBuffer *buffer) /* Even if we already have the selection, we need to update our * timestamp. */ - if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets), + if (!gtk_clipboard_set_with_owner (clipboard, + priv->copy_target_entries, + priv->n_copy_target_entries, clipboard_get_selection_cb, clipboard_clear_selection_cb, G_OBJECT (buffer))) @@ -3121,7 +3682,7 @@ static SelectionClipboard * find_selection_clipboard (GtkTextBuffer *buffer, GtkClipboard *clipboard) { - GSList *tmp_list = buffer->selection_clipboards; + GSList *tmp_list = buffer->priv->selection_clipboards; while (tmp_list) { SelectionClipboard *selection_clipboard = tmp_list->data; @@ -3139,9 +3700,9 @@ find_selection_clipboard (GtkTextBuffer *buffer, * @buffer: a #GtkTextBuffer * @clipboard: a #GtkClipboard * - * Adds @clipboard to the list of clipboards in which the selection contents - * of @buffer are available. In most cases, @clipboard will be the #GtkClipboard - * of type %GDK_SELECTION_PRIMARY for a view of @buffer. + * Adds @clipboard to the list of clipboards in which the selection + * contents of @buffer are available. In most cases, @clipboard will be + * the #GtkClipboard of type %GDK_SELECTION_PRIMARY for a view of @buffer. **/ void gtk_text_buffer_add_selection_clipboard (GtkTextBuffer *buffer, @@ -3164,16 +3725,19 @@ gtk_text_buffer_add_selection_clipboard (GtkTextBuffer *buffer, selection_clipboard->clipboard = clipboard; selection_clipboard->ref_count = 1; - buffer->selection_clipboards = g_slist_prepend (buffer->selection_clipboards, selection_clipboard); + buffer->priv->selection_clipboards = g_slist_prepend (buffer->priv->selection_clipboards, + selection_clipboard); } } /** * gtk_text_buffer_remove_selection_clipboard: * @buffer: a #GtkTextBuffer - * @clipboard: a #GtkClipboard added to @buffer by gtk_text_buffer_add_selection_clipboard(). + * @clipboard: a #GtkClipboard added to @buffer by + * gtk_text_buffer_add_selection_clipboard() * - * Removes a #GtkClipboard added with gtk_text_buffer_add_selection_clipboard() + * Removes a #GtkClipboard added with + * gtk_text_buffer_add_selection_clipboard(). **/ void gtk_text_buffer_remove_selection_clipboard (GtkTextBuffer *buffer, @@ -3193,8 +3757,8 @@ gtk_text_buffer_remove_selection_clipboard (GtkTextBuffer *buffer, if (gtk_clipboard_get_owner (selection_clipboard->clipboard) == G_OBJECT (buffer)) gtk_clipboard_clear (selection_clipboard->clipboard); - buffer->selection_clipboards = g_slist_remove (buffer->selection_clipboards, - selection_clipboard); + buffer->priv->selection_clipboards = g_slist_remove (buffer->priv->selection_clipboards, + selection_clipboard); g_free (selection_clipboard); } @@ -3203,35 +3767,25 @@ gtk_text_buffer_remove_selection_clipboard (GtkTextBuffer *buffer, static void remove_all_selection_clipboards (GtkTextBuffer *buffer) { - GSList *tmp_list = buffer->selection_clipboards; - while (tmp_list) - { - SelectionClipboard *selection_clipboard = tmp_list->data; - - if (gtk_clipboard_get_owner (selection_clipboard->clipboard) == G_OBJECT (buffer)) - gtk_clipboard_clear (selection_clipboard->clipboard); - - g_free (selection_clipboard); - - tmp_list = tmp_list->next; - } + GtkTextBufferPrivate *priv = buffer->priv; - g_slist_free (buffer->selection_clipboards); - buffer->selection_clipboards = NULL; + g_slist_foreach (priv->selection_clipboards, (GFunc)g_free, NULL); + g_slist_free (priv->selection_clipboards); + priv->selection_clipboards = NULL; } /** * gtk_text_buffer_paste_clipboard: * @buffer: a #GtkTextBuffer * @clipboard: the #GtkClipboard to paste from - * @override_location: location to insert pasted text, or %NULL for at the cursor + * @override_location: (allow-none): location to insert pasted text, or %NULL for + * at the cursor * @default_editable: whether the buffer is editable by default * - * Pastes the contents of a clipboard at the insertion point, or at @override_location. - * (Note: pasting is asynchronous, that is, we'll ask for the paste data and - * return, and at some point later after the main loop runs, the paste - * data will be inserted.) - * + * Pastes the contents of a clipboard at the insertion point, or + * at @override_location. (Note: pasting is asynchronous, that is, + * we'll ask for the paste data and return, and at some point later + * after the main loop runs, the paste data will be inserted.) **/ void gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer, @@ -3248,10 +3802,9 @@ gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer, "gtk_paste_point_override", override_location, FALSE); - data->buffer = buffer; - g_object_ref (buffer); + data->buffer = g_object_ref (buffer); data->interactive = TRUE; - data->default_editable = default_editable; + data->default_editable = !!default_editable; /* When pasting with the cursor inside the selection area, you * replace the selection with the new text, otherwise, you @@ -3268,7 +3821,7 @@ gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer, data->replace_selection = TRUE; gtk_clipboard_request_contents (clipboard, - gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE), + gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"), clipboard_clipboard_buffer_received, data); } @@ -3300,11 +3853,7 @@ gtk_text_buffer_delete_selection (GtkTextBuffer *buffer, else { if (interactive) - { - gtk_text_buffer_begin_user_action (buffer); - gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable); - gtk_text_buffer_end_user_action (buffer); - } + gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable); else gtk_text_buffer_delete (buffer, &start, &end); @@ -3331,7 +3880,7 @@ gtk_text_buffer_delete_selection (GtkTextBuffer *buffer, * re-initialized to point to the location where text was deleted. * * Return value: %TRUE if the buffer was modified - + * * Since: 2.6 **/ gboolean @@ -3380,13 +3929,14 @@ gtk_text_buffer_backspace (GtkTextBuffer *buffer, if (gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable)) { - if (backspace_deletes_character) + /* special case \r\n, since we never want to reinsert \r */ + if (backspace_deletes_character && strcmp ("\r\n", cluster_text)) { gchar *normalized_text = g_utf8_normalize (cluster_text, strlen (cluster_text), G_NORMALIZE_NFD); glong len = g_utf8_strlen (normalized_text, -1); - + if (len > 1) gtk_text_buffer_insert_interactive (buffer, &start, @@ -3418,6 +3968,8 @@ cut_or_copy (GtkTextBuffer *buffer, gboolean interactive, gboolean default_editable) { + GtkTextBufferPrivate *priv; + /* We prefer to cut the selected region between selection_bound and * insertion point. If that region is empty, then we cut the region * between the "anchor" and the insertion point (this is for @@ -3427,7 +3979,11 @@ cut_or_copy (GtkTextBuffer *buffer, */ GtkTextIter start; GtkTextIter end; - + + priv = buffer->priv; + + gtk_text_buffer_get_copy_target_list (buffer); + if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end)) { /* Let's try the anchor thing */ @@ -3453,14 +4009,18 @@ cut_or_copy (GtkTextBuffer *buffer, gtk_text_buffer_insert_range (contents, &ins, &start, &end); - if (!gtk_clipboard_set_with_data (clipboard, targets, G_N_ELEMENTS (targets), + if (!gtk_clipboard_set_with_data (clipboard, + priv->copy_target_entries, + priv->n_copy_target_entries, clipboard_get_contents_cb, clipboard_clear_contents_cb, contents)) g_object_unref (contents); else - gtk_clipboard_set_can_store (clipboard, (GtkTargetEntry *)targets, G_N_ELEMENTS (targets) -1); - + gtk_clipboard_set_can_store (clipboard, + priv->copy_target_entries + 1, + priv->n_copy_target_entries - 1); + if (delete_region_after) { if (interactive) @@ -3475,12 +4035,11 @@ cut_or_copy (GtkTextBuffer *buffer, /** * gtk_text_buffer_cut_clipboard: * @buffer: a #GtkTextBuffer - * @clipboard: the #GtkClipboard object to cut to. + * @clipboard: the #GtkClipboard object to cut to * @default_editable: default editability of the buffer * * Copies the currently-selected text to a clipboard, then deletes * said text if it's editable. - * **/ void gtk_text_buffer_cut_clipboard (GtkTextBuffer *buffer, @@ -3495,26 +4054,22 @@ gtk_text_buffer_cut_clipboard (GtkTextBuffer *buffer, /** * gtk_text_buffer_copy_clipboard: * @buffer: a #GtkTextBuffer - * @clipboard: the #GtkClipboard object to copy to. + * @clipboard: the #GtkClipboard object to copy to * * Copies the currently-selected text to a clipboard. - * **/ void gtk_text_buffer_copy_clipboard (GtkTextBuffer *buffer, GtkClipboard *clipboard) { - gtk_text_buffer_begin_user_action (buffer); cut_or_copy (buffer, clipboard, FALSE, TRUE, TRUE); - gtk_text_buffer_end_user_action (buffer); } - /** * gtk_text_buffer_get_selection_bounds: * @buffer: a #GtkTextBuffer a #GtkTextBuffer - * @start: iterator to initialize with selection start - * @end: iterator to initialize with selection end + * @start: (out): iterator to initialize with selection start + * @end: (out): iterator to initialize with selection end * * Returns %TRUE if some text is selected; places the bounds * of the selection in @start and @end (if the selection has length 0, @@ -3526,9 +4081,9 @@ gtk_text_buffer_copy_clipboard (GtkTextBuffer *buffer, * Return value: whether the selection has nonzero length **/ gboolean -gtk_text_buffer_get_selection_bounds (GtkTextBuffer *buffer, - GtkTextIter *start, - GtkTextIter *end) +gtk_text_buffer_get_selection_bounds (GtkTextBuffer *buffer, + GtkTextIter *start, + GtkTextIter *end) { g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE); @@ -3546,9 +4101,10 @@ gtk_text_buffer_get_selection_bounds (GtkTextBuffer *buffer, * gtk_text_buffer_end_user_action() can then be grouped when creating * an undo stack. #GtkTextBuffer maintains a count of calls to * gtk_text_buffer_begin_user_action() that have not been closed with - * a call to gtk_text_buffer_end_user_action(), and emits the "begin_user_action" - * and "end_user_action" signals only for the outermost pair of calls. - * This allows you to build user actions from other user actions. + * a call to gtk_text_buffer_end_user_action(), and emits the + * "begin-user-action" and "end-user-action" signals only for the + * outermost pair of calls. This allows you to build user actions + * from other user actions. * * The "interactive" buffer mutation functions, such as * gtk_text_buffer_insert_interactive(), automatically call begin/end @@ -3561,9 +4117,9 @@ gtk_text_buffer_begin_user_action (GtkTextBuffer *buffer) { g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); - buffer->user_action_count += 1; + buffer->priv->user_action_count += 1; - if (buffer->user_action_count == 1) + if (buffer->priv->user_action_count == 1) { /* Outermost nested user action begin emits the signal */ g_signal_emit (buffer, signals[BEGIN_USER_ACTION], 0); @@ -3581,17 +4137,137 @@ void gtk_text_buffer_end_user_action (GtkTextBuffer *buffer) { g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); - g_return_if_fail (buffer->user_action_count > 0); + g_return_if_fail (buffer->priv->user_action_count > 0); - buffer->user_action_count -= 1; + buffer->priv->user_action_count -= 1; - if (buffer->user_action_count == 0) + if (buffer->priv->user_action_count == 0) { /* Ended the outermost-nested user action end, so emit the signal */ g_signal_emit (buffer, signals[END_USER_ACTION], 0); } } +static void +gtk_text_buffer_free_target_lists (GtkTextBuffer *buffer) +{ + GtkTextBufferPrivate *priv = buffer->priv; + + if (priv->copy_target_list) + { + gtk_target_list_unref (priv->copy_target_list); + priv->copy_target_list = NULL; + + gtk_target_table_free (priv->copy_target_entries, + priv->n_copy_target_entries); + priv->copy_target_entries = NULL; + priv->n_copy_target_entries = 0; + } + + if (priv->paste_target_list) + { + gtk_target_list_unref (priv->paste_target_list); + priv->paste_target_list = NULL; + + gtk_target_table_free (priv->paste_target_entries, + priv->n_paste_target_entries); + priv->paste_target_entries = NULL; + priv->n_paste_target_entries = 0; + } +} + +static GtkTargetList * +gtk_text_buffer_get_target_list (GtkTextBuffer *buffer, + gboolean deserializable, + GtkTargetEntry **entries, + gint *n_entries) +{ + GtkTargetList *target_list; + + target_list = gtk_target_list_new (NULL, 0); + + gtk_target_list_add (target_list, + gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"), + GTK_TARGET_SAME_APP, + GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS); + + gtk_target_list_add_rich_text_targets (target_list, + GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT, + deserializable, + buffer); + + gtk_target_list_add_text_targets (target_list, + GTK_TEXT_BUFFER_TARGET_INFO_TEXT); + + *entries = gtk_target_table_new_from_list (target_list, n_entries); + + return target_list; +} + +/** + * gtk_text_buffer_get_copy_target_list: + * @buffer: a #GtkTextBuffer + * + * This function returns the list of targets this text buffer can + * provide for copying and as DND source. The targets in the list are + * added with %info values from the #GtkTextBufferTargetInfo enum, + * using gtk_target_list_add_rich_text_targets() and + * gtk_target_list_add_text_targets(). + * + * Return value: (transfer none): the #GtkTargetList + * + * Since: 2.10 + **/ +GtkTargetList * +gtk_text_buffer_get_copy_target_list (GtkTextBuffer *buffer) +{ + GtkTextBufferPrivate *priv; + + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); + + priv = buffer->priv; + + if (! priv->copy_target_list) + priv->copy_target_list = + gtk_text_buffer_get_target_list (buffer, FALSE, + &priv->copy_target_entries, + &priv->n_copy_target_entries); + + return priv->copy_target_list; +} + +/** + * gtk_text_buffer_get_paste_target_list: + * @buffer: a #GtkTextBuffer + * + * This function returns the list of targets this text buffer supports + * for pasting and as DND destination. The targets in the list are + * added with %info values from the #GtkTextBufferTargetInfo enum, + * using gtk_target_list_add_rich_text_targets() and + * gtk_target_list_add_text_targets(). + * + * Return value: (transfer none): the #GtkTargetList + * + * Since: 2.10 + **/ +GtkTargetList * +gtk_text_buffer_get_paste_target_list (GtkTextBuffer *buffer) +{ + GtkTextBufferPrivate *priv; + + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); + + priv = buffer->priv; + + if (! priv->paste_target_list) + priv->paste_target_list = + gtk_text_buffer_get_target_list (buffer, TRUE, + &priv->paste_target_entries, + &priv->n_paste_target_entries); + + return priv->paste_target_list; +} + /* * Logical attribute cache */ @@ -3606,7 +4282,6 @@ struct _CacheEntry PangoLogAttr *attrs; }; - struct _GtkTextLogAttrCache { gint chars_changed_stamp; @@ -3684,6 +4359,7 @@ _gtk_text_buffer_get_line_log_attrs (GtkTextBuffer *buffer, const GtkTextIter *anywhere_in_line, gint *char_len) { + GtkTextBufferPrivate *priv; gint line; GtkTextLogAttrCache *cache; gint i; @@ -3691,6 +4367,8 @@ _gtk_text_buffer_get_line_log_attrs (GtkTextBuffer *buffer, g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); g_return_val_if_fail (anywhere_in_line != NULL, NULL); + priv = buffer->priv; + /* special-case for empty last line in buffer */ if (gtk_text_iter_is_end (anywhere_in_line) && gtk_text_iter_get_line_offset (anywhere_in_line) == 0) @@ -3704,19 +4382,19 @@ _gtk_text_buffer_get_line_log_attrs (GtkTextBuffer *buffer, * the start of a paragraph changes */ - if (buffer->log_attr_cache == NULL) + if (priv->log_attr_cache == NULL) { - buffer->log_attr_cache = g_new0 (GtkTextLogAttrCache, 1); - buffer->log_attr_cache->chars_changed_stamp = + priv->log_attr_cache = g_new0 (GtkTextLogAttrCache, 1); + priv->log_attr_cache->chars_changed_stamp = _gtk_text_btree_get_chars_changed_stamp (get_btree (buffer)); } - else if (buffer->log_attr_cache->chars_changed_stamp != + else if (priv->log_attr_cache->chars_changed_stamp != _gtk_text_btree_get_chars_changed_stamp (get_btree (buffer))) { - clear_log_attr_cache (buffer->log_attr_cache); + clear_log_attr_cache (priv->log_attr_cache); } - cache = buffer->log_attr_cache; + cache = priv->log_attr_cache; line = gtk_text_iter_get_line (anywhere_in_line); i = 0; @@ -3733,8 +4411,7 @@ _gtk_text_buffer_get_line_log_attrs (GtkTextBuffer *buffer, } /* Not in cache; open up the first cache entry */ - if (cache->entries[ATTR_CACHE_SIZE-1].attrs) - g_free (cache->entries[ATTR_CACHE_SIZE-1].attrs); + g_free (cache->entries[ATTR_CACHE_SIZE-1].attrs); g_memmove (cache->entries + 1, cache->entries, sizeof (CacheEntry) * (ATTR_CACHE_SIZE - 1)); @@ -3754,12 +4431,12 @@ _gtk_text_buffer_notify_will_remove_tag (GtkTextBuffer *buffer, GtkTextTag *tag) { /* This removes tag from the buffer, but DOESN'T emit the - * remove_tag signal, because we can't afford to have user + * remove-tag signal, because we can't afford to have user * code messing things up at this point; the tag MUST be removed * entirely. */ - if (buffer->btree) - _gtk_text_btree_notify_will_remove_tag (buffer->btree, tag); + if (buffer->priv->btree) + _gtk_text_btree_notify_will_remove_tag (buffer->priv->btree, tag); } /* @@ -3772,5 +4449,301 @@ _gtk_text_buffer_spew (GtkTextBuffer *buffer) _gtk_text_btree_spew (get_btree (buffer)); } -#define __GTK_TEXT_BUFFER_C__ -#include "gtkaliasdef.c" +void +_gtk_text_buffer_get_text_before (GtkTextBuffer *buffer, + AtkTextBoundary boundary_type, + GtkTextIter *position, + GtkTextIter *start, + GtkTextIter *end) +{ + gint line_number; + + *start = *position; + *end = *start; + + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + gtk_text_iter_backward_char (start); + break; + + case ATK_TEXT_BOUNDARY_WORD_START: + if (!gtk_text_iter_starts_word (start)) + gtk_text_iter_backward_word_start (start); + *end = *start; + gtk_text_iter_backward_word_start (start); + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + if (gtk_text_iter_inside_word (start) && + !gtk_text_iter_starts_word (start)) + gtk_text_iter_backward_word_start (start); + while (!gtk_text_iter_ends_word (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + *end = *start; + gtk_text_iter_backward_word_start (start); + while (!gtk_text_iter_ends_word (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + break; + + case ATK_TEXT_BOUNDARY_SENTENCE_START: + if (!gtk_text_iter_starts_sentence (start)) + gtk_text_iter_backward_sentence_start (start); + *end = *start; + gtk_text_iter_backward_sentence_start (start); + break; + + case ATK_TEXT_BOUNDARY_SENTENCE_END: + if (gtk_text_iter_inside_sentence (start) && + !gtk_text_iter_starts_sentence (start)) + gtk_text_iter_backward_sentence_start (start); + while (!gtk_text_iter_ends_sentence (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + *end = *start; + gtk_text_iter_backward_sentence_start (start); + while (!gtk_text_iter_ends_sentence (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + break; + + case ATK_TEXT_BOUNDARY_LINE_START: + line_number = gtk_text_iter_get_line (start); + if (line_number == 0) + { + gtk_text_buffer_get_iter_at_offset (buffer, start, 0); + } + else + { + gtk_text_iter_backward_line (start); + gtk_text_iter_forward_line (start); + } + *end = *start; + gtk_text_iter_backward_line (start); + break; + + case ATK_TEXT_BOUNDARY_LINE_END: + line_number = gtk_text_iter_get_line (start); + if (line_number == 0) + { + gtk_text_buffer_get_iter_at_offset (buffer, start, 0); + *end = *start; + } + else + { + gtk_text_iter_backward_line (start); + *end = *start; + while (!gtk_text_iter_ends_line (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + gtk_text_iter_forward_to_line_end (end); + } + break; + } +} + +void +_gtk_text_buffer_get_text_at (GtkTextBuffer *buffer, + AtkTextBoundary boundary_type, + GtkTextIter *position, + GtkTextIter *start, + GtkTextIter *end) +{ + gint line_number; + + *start = *position; + *end = *start; + + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + gtk_text_iter_forward_char (end); + break; + + case ATK_TEXT_BOUNDARY_WORD_START: + if (!gtk_text_iter_starts_word (start)) + gtk_text_iter_backward_word_start (start); + if (gtk_text_iter_inside_word (end)) + gtk_text_iter_forward_word_end (end); + while (!gtk_text_iter_starts_word (end)) + { + if (!gtk_text_iter_forward_char (end)) + break; + } + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + if (gtk_text_iter_inside_word (start) && + !gtk_text_iter_starts_word (start)) + gtk_text_iter_backward_word_start (start); + while (!gtk_text_iter_ends_word (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + gtk_text_iter_forward_word_end (end); + break; + + case ATK_TEXT_BOUNDARY_SENTENCE_START: + if (!gtk_text_iter_starts_sentence (start)) + gtk_text_iter_backward_sentence_start (start); + if (gtk_text_iter_inside_sentence (end)) + gtk_text_iter_forward_sentence_end (end); + while (!gtk_text_iter_starts_sentence (end)) + { + if (!gtk_text_iter_forward_char (end)) + break; + } + break; + + case ATK_TEXT_BOUNDARY_SENTENCE_END: + if (gtk_text_iter_inside_sentence (start) && + !gtk_text_iter_starts_sentence (start)) + gtk_text_iter_backward_sentence_start (start); + while (!gtk_text_iter_ends_sentence (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + gtk_text_iter_forward_sentence_end (end); + break; + + case ATK_TEXT_BOUNDARY_LINE_START: + line_number = gtk_text_iter_get_line (start); + if (line_number == 0) + { + gtk_text_buffer_get_iter_at_offset (buffer, start, 0); + } + else + { + gtk_text_iter_backward_line (start); + gtk_text_iter_forward_line (start); + } + gtk_text_iter_forward_line (end); + break; + + case ATK_TEXT_BOUNDARY_LINE_END: + line_number = gtk_text_iter_get_line (start); + if (line_number == 0) + { + gtk_text_buffer_get_iter_at_offset (buffer, start, 0); + } + else + { + gtk_text_iter_backward_line (start); + gtk_text_iter_forward_line (start); + } + while (!gtk_text_iter_ends_line (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + gtk_text_iter_forward_to_line_end (end); + break; + } +} + +void +_gtk_text_buffer_get_text_after (GtkTextBuffer *buffer, + AtkTextBoundary boundary_type, + GtkTextIter *position, + GtkTextIter *start, + GtkTextIter *end) +{ + *start = *position; + *end = *start; + + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + gtk_text_iter_forward_char (start); + gtk_text_iter_forward_chars (end, 2); + break; + + case ATK_TEXT_BOUNDARY_WORD_START: + if (gtk_text_iter_inside_word (end)) + gtk_text_iter_forward_word_end (end); + while (!gtk_text_iter_starts_word (end)) + { + if (!gtk_text_iter_forward_char (end)) + break; + } + *start = *end; + if (!gtk_text_iter_is_end (end)) + { + gtk_text_iter_forward_word_end (end); + while (!gtk_text_iter_starts_word (end)) + { + if (!gtk_text_iter_forward_char (end)) + break; + } + } + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + gtk_text_iter_forward_word_end (end); + *start = *end; + if (!gtk_text_iter_is_end (end)) + gtk_text_iter_forward_word_end (end); + break; + + case ATK_TEXT_BOUNDARY_SENTENCE_START: + if (gtk_text_iter_inside_sentence (end)) + gtk_text_iter_forward_sentence_end (end); + while (!gtk_text_iter_starts_sentence (end)) + { + if (!gtk_text_iter_forward_char (end)) + break; + } + *start = *end; + if (!gtk_text_iter_is_end (end)) + { + gtk_text_iter_forward_sentence_end (end); + while (!gtk_text_iter_starts_sentence (end)) + { + if (!gtk_text_iter_forward_char (end)) + break; + } + } + break; + + case ATK_TEXT_BOUNDARY_SENTENCE_END: + gtk_text_iter_forward_sentence_end (end); + *start = *end; + if (!gtk_text_iter_is_end (end)) + gtk_text_iter_forward_sentence_end (end); + break; + + case ATK_TEXT_BOUNDARY_LINE_START: + gtk_text_iter_forward_line (end); + *start = *end; + gtk_text_iter_forward_line (end); + break; + + case ATK_TEXT_BOUNDARY_LINE_END: + gtk_text_iter_forward_line (start); + *end = *start; + if (!gtk_text_iter_is_end (start)) + { + while (!gtk_text_iter_ends_line (start)) + { + if (!gtk_text_iter_backward_char (start)) + break; + } + gtk_text_iter_forward_to_line_end (end); + } + break; + } +}