From 6ac30db004c4a768aaf74286bcc21ff7123e0550 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 26 May 2007 04:10:42 +0000 Subject: [PATCH] Allow to separate GtkTextMark creation from buffer insertion. (#132818, MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2007-05-25 Matthias Clasen Allow to separate GtkTextMark creation from buffer insertion. (#132818, Gustavo Giráldez, patch by Yevgen Muntyan) * gtk/gtktextmarkprivate.h: * gtk/gtktextmark.[hc] (gtk_text_mark_new): New function to create a GtkTextMark. * gtk/gtktextbuffer.[hc] (gtk_text_buffer_add_mark): New function to add an existing mark to a buffer. * gtk/gtktextbtree.c: Allow adding existing marks. * gtk/gtk.symbols: Add new functions. * tests/testtextbuffer.c: Add some tests for new mark functionality. svn path=/trunk/; revision=17922 --- ChangeLog | 19 ++++ docs/reference/ChangeLog | 4 + docs/reference/gtk/gtk-sections.txt | 2 + gtk/gtk.symbols | 2 + gtk/gtktextbtree.c | 20 +++- gtk/gtktextbuffer.c | 50 +++++++- gtk/gtktextbuffer.h | 3 + gtk/gtktextmark.c | 170 ++++++++++++++++++++++++---- gtk/gtktextmark.h | 2 + gtk/gtktextmarkprivate.h | 5 +- tests/testtextbuffer.c | 75 ++++++++++++ 11 files changed, 317 insertions(+), 35 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9aeb1335dc..ece998228f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2007-05-25 Matthias Clasen + + Allow to separate GtkTextMark creation from buffer insertion. + (#132818, Gustavo Giráldez, patch by Yevgen Muntyan) + + * gtk/gtktextmarkprivate.h: + * gtk/gtktextmark.[hc] (gtk_text_mark_new): New function to + create a GtkTextMark. + + * gtk/gtktextbuffer.[hc] (gtk_text_buffer_add_mark): New + function to add an existing mark to a buffer. + + * gtk/gtktextbtree.c: Allow adding existing marks. + + * gtk/gtk.symbols: Add new functions. + + * tests/testtextbuffer.c: Add some tests for new mark + functionality. + 2007-05-25 Xan Lopez * gtk/gtkaction.c: (gtk_action_set_short_label): diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index cbec4c0164..8a94a29a6c 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,7 @@ +2007-05-25 Matthias Clasen + + * gtk/gtk-sections.txt: Add new functions + 2007-05-24 Matthias Clasen * === Released 2.11.0 === diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt index 37fbeea6d3..f4059825b9 100644 --- a/docs/reference/gtk/gtk-sections.txt +++ b/docs/reference/gtk/gtk-sections.txt @@ -3411,6 +3411,7 @@ gtk_text_buffer_create_child_anchor gtk_text_buffer_create_mark gtk_text_buffer_move_mark gtk_text_buffer_move_mark_by_name +gtk_text_buffer_add_mark gtk_text_buffer_delete_mark gtk_text_buffer_delete_mark_by_name gtk_text_buffer_get_mark @@ -3597,6 +3598,7 @@ gtk_text_iter_get_type gtktextmark GtkTextMark GtkTextMark +gtk_text_mark_new gtk_text_mark_set_visible gtk_text_mark_get_visible gtk_text_mark_get_deleted diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 9f7e02fe15..5b28f13185 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -3552,6 +3552,7 @@ gtk_text_buffer_backspace gtk_text_buffer_begin_user_action gtk_text_buffer_copy_clipboard gtk_text_buffer_create_child_anchor +gtk_text_buffer_add_mark gtk_text_buffer_create_mark gtk_text_buffer_create_tag gtk_text_buffer_cut_clipboard @@ -3789,6 +3790,7 @@ gtk_text_line_segment_split #if IN_HEADER(__GTK_TEXT_MARK_H__) #if IN_FILE(__GTK_TEXT_MARK_C__) +gtk_text_mark_new gtk_text_mark_get_buffer gtk_text_mark_get_deleted gtk_text_mark_get_left_gravity diff --git a/gtk/gtktextbtree.c b/gtk/gtktextbtree.c index 7601803371..93f6b4b3d9 100644 --- a/gtk/gtktextbtree.c +++ b/gtk/gtktextbtree.c @@ -2692,7 +2692,12 @@ real_set_mark (GtkTextBTree *tree, g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL); if (existing_mark) - mark = existing_mark->segment; + { + if (gtk_text_mark_get_buffer (existing_mark) != NULL) + mark = existing_mark->segment; + else + mark = NULL; + } else if (name != NULL) mark = g_hash_table_lookup (tree->mark_table, name); @@ -2752,9 +2757,13 @@ real_set_mark (GtkTextBTree *tree, } else { - mark = _gtk_mark_segment_new (tree, - left_gravity, - name); + if (existing_mark) + g_object_ref (existing_mark); + else + existing_mark = gtk_text_mark_new (name, left_gravity); + + mark = existing_mark->segment; + _gtk_mark_segment_set_tree (mark, tree); mark->body.mark.line = _gtk_text_iter_get_text_line (&iter); @@ -2980,7 +2989,8 @@ gtk_text_mark_set_visible (GtkTextMark *mark, { seg->body.mark.visible = setting; - redisplay_mark (seg); + if (seg->body.mark.tree) + redisplay_mark (seg); } } diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c index 20e34be08b..68364b60b1 100644 --- a/gtk/gtktextbuffer.c +++ b/gtk/gtktextbuffer.c @@ -2079,6 +2079,44 @@ 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 @@ -2129,13 +2167,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, diff --git a/gtk/gtktextbuffer.h b/gtk/gtktextbuffer.h index 715b514acf..9a0eb9d28e 100644 --- a/gtk/gtktextbuffer.h +++ b/gtk/gtktextbuffer.h @@ -247,6 +247,9 @@ GtkTextChildAnchor *gtk_text_buffer_create_child_anchor (GtkTextBuffer *buffer, GtkTextIter *iter); /* Mark manipulation */ +void gtk_text_buffer_add_mark (GtkTextBuffer *buffer, + GtkTextMark *mark, + const GtkTextIter *where); GtkTextMark *gtk_text_buffer_create_mark (GtkTextBuffer *buffer, const gchar *mark_name, const GtkTextIter *where, diff --git a/gtk/gtktextmark.c b/gtk/gtktextmark.c index 281538e3f9..febec6b4b3 100644 --- a/gtk/gtktextmark.c +++ b/gtk/gtktextmark.c @@ -50,18 +50,29 @@ #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API #include #include "gtktextbtree.h" +#include "gtkprivate.h" #include "gtkintl.h" #include "gtkalias.h" -static void gtk_text_mark_finalize (GObject *obj); +static void gtk_text_mark_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_text_mark_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gtk_text_mark_finalize (GObject *object); + +static GtkTextLineSegment *gtk_mark_segment_new (GtkTextMark *mark_obj); G_DEFINE_TYPE (GtkTextMark, gtk_text_mark, G_TYPE_OBJECT) -static void -gtk_text_mark_init (GtkTextMark *mark) -{ - mark->segment = NULL; -} +enum { + PROP_0, + PROP_NAME, + PROP_LEFT_GRAVITY +}; static void gtk_text_mark_class_init (GtkTextMarkClass *klass) @@ -69,6 +80,30 @@ gtk_text_mark_class_init (GtkTextMarkClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gtk_text_mark_finalize; + object_class->set_property = gtk_text_mark_set_property; + object_class->get_property = gtk_text_mark_get_property; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + P_("Name"), + P_("Mark name"), + NULL, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_LEFT_GRAVITY, + g_param_spec_boolean ("left-gravity", + P_("Left gravity"), + P_("Whether the mark has left gravity"), + FALSE, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gtk_text_mark_init (GtkTextMark *mark) +{ + mark->segment = gtk_mark_segment_new (mark); } static void @@ -98,6 +133,88 @@ gtk_text_mark_finalize (GObject *obj) G_OBJECT_CLASS (gtk_text_mark_parent_class)->finalize (obj); } +static void +gtk_text_mark_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + gchar *tmp; + GtkTextMark *mark = GTK_TEXT_MARK (object); + GtkTextLineSegment *seg = mark->segment; + + switch (prop_id) + { + case PROP_NAME: + tmp = seg->body.mark.name; + seg->body.mark.name = g_value_dup_string (value); + g_free (tmp); + break; + + case PROP_LEFT_GRAVITY: + if (g_value_get_boolean (value)) + seg->type = >k_text_left_mark_type; + else + seg->type = >k_text_right_mark_type; + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtk_text_mark_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkTextMark *mark = GTK_TEXT_MARK (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, gtk_text_mark_get_name (mark)); + break; + + case PROP_LEFT_GRAVITY: + g_value_set_boolean (value, gtk_text_mark_get_left_gravity (mark)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +/** + * gtk_text_mark_new: + * @name: mark name or %NULL + * @left_gravity: whether the mark should have left gravity + * + * Creates a text mark. Add it to a buffer using gtk_text_buffer_add_mark(). + * If @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 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 newly-inserted text. The standard left-to-right cursor is a + * mark with right gravity (when you type, the cursor stays on the right + * side of the text you're typing). + * + * Return value: new #GtkTextMark + * + * Since: 2.12 + **/ +GtkTextMark * +gtk_text_mark_new (const gchar *name, + gboolean left_gravity) +{ + return g_object_new (GTK_TYPE_TEXT_MARK, + "name", name, + "left-gravity", left_gravity, + NULL); +} + /** * gtk_text_mark_get_visible: * @mark: a #GtkTextMark @@ -140,8 +257,8 @@ gtk_text_mark_get_name (GtkTextMark *mark) * @mark: a #GtkTextMark * * Returns %TRUE if the mark has been removed from its buffer - * with gtk_text_buffer_delete_mark(). Marks can't be used - * once deleted. + * with gtk_text_buffer_delete_mark(). See gtk_text_buffer_add_mark() + * for a way to add it to a buffer again. * * Return value: whether the mark is deleted **/ @@ -212,28 +329,22 @@ gtk_text_mark_get_left_gravity (GtkTextMark *mark) + sizeof (GtkTextMarkBody))) -GtkTextLineSegment* -_gtk_mark_segment_new (GtkTextBTree *tree, - gboolean left_gravity, - const gchar *name) +static GtkTextLineSegment * +gtk_mark_segment_new (GtkTextMark *mark_obj) { GtkTextLineSegment *mark; mark = (GtkTextLineSegment *) g_malloc0 (MSEG_SIZE); - mark->body.mark.name = g_strdup (name); - - if (left_gravity) - mark->type = >k_text_left_mark_type; - else - mark->type = >k_text_right_mark_type; + mark->body.mark.name = NULL; + mark->type = >k_text_right_mark_type; mark->byte_count = 0; mark->char_count = 0; - mark->body.mark.obj = g_object_new (GTK_TYPE_TEXT_MARK, NULL); - mark->body.mark.obj->segment = mark; + mark->body.mark.obj = mark_obj; + mark_obj->segment = mark; - mark->body.mark.tree = tree; + mark->body.mark.tree = NULL; mark->body.mark.line = NULL; mark->next = NULL; @@ -243,6 +354,23 @@ _gtk_mark_segment_new (GtkTextBTree *tree, return mark; } +void +_gtk_mark_segment_set_tree (GtkTextLineSegment *mark, + GtkTextBTree *tree) +{ + g_assert (mark->body.mark.tree == NULL); + g_assert (mark->body.mark.obj != NULL); + + mark->byte_count = 0; + mark->char_count = 0; + + mark->body.mark.tree = tree; + mark->body.mark.line = NULL; + mark->next = NULL; + + mark->body.mark.not_deleteable = FALSE; +} + static int mark_segment_delete_func (GtkTextLineSegment *segPtr, GtkTextLine *line, int treeGone); diff --git a/gtk/gtktextmark.h b/gtk/gtktextmark.h index 851123c4ac..41bcb9492f 100644 --- a/gtk/gtktextmark.h +++ b/gtk/gtktextmark.h @@ -88,6 +88,8 @@ void gtk_text_mark_set_visible (GtkTextMark *mark, gboolean setting); gboolean gtk_text_mark_get_visible (GtkTextMark *mark); +GtkTextMark *gtk_text_mark_new (const gchar *name, + gboolean left_gravity); G_CONST_RETURN gchar* gtk_text_mark_get_name (GtkTextMark *mark); gboolean gtk_text_mark_get_deleted (GtkTextMark *mark); GtkTextBuffer* gtk_text_mark_get_buffer (GtkTextMark *mark); diff --git a/gtk/gtktextmarkprivate.h b/gtk/gtktextmarkprivate.h index 43e9797366..d2ff381371 100644 --- a/gtk/gtktextmarkprivate.h +++ b/gtk/gtktextmarkprivate.h @@ -49,9 +49,8 @@ struct _GtkTextMarkBody { guint not_deleteable : 1; }; -GtkTextLineSegment *_gtk_mark_segment_new (GtkTextBTree *tree, - gboolean left_gravity, - const gchar *name); +void _gtk_mark_segment_set_tree (GtkTextLineSegment *mark, + GtkTextBTree *tree); G_END_DECLS diff --git a/tests/testtextbuffer.c b/tests/testtextbuffer.c index b739deadcc..eaae8b7352 100644 --- a/tests/testtextbuffer.c +++ b/tests/testtextbuffer.c @@ -48,6 +48,8 @@ static void line_separator_tests (void); static void logical_motion_tests (void); +static void mark_tests (void); + int main (int argc, char** argv) { @@ -75,6 +77,9 @@ main (int argc, char** argv) /* Create a buffer */ buffer = gtk_text_buffer_new (NULL); + /* Marks */ + mark_tests (); + /* Check that buffer starts with one empty line and zero chars */ n = gtk_text_buffer_get_line_count (buffer); @@ -1210,3 +1215,73 @@ logical_motion_tests (void) g_object_unref (buffer); } + +static void +mark_tests (void) +{ + GtkTextBuffer *buf1, *buf2; + GtkTextMark *mark; + GtkTextIter iter; + + buf1 = gtk_text_buffer_new (NULL); + buf2 = gtk_text_buffer_new (NULL); + + gtk_text_buffer_get_start_iter (buf1, &iter); + mark = gtk_text_buffer_create_mark (buf1, "foo", &iter, TRUE); + g_object_ref (mark); + gtk_text_mark_set_visible (mark, TRUE); + gtk_text_buffer_delete_mark (buf1, mark); + + g_assert (gtk_text_mark_get_visible (mark)); + g_assert (gtk_text_mark_get_left_gravity (mark)); + g_assert (!strcmp ("foo", gtk_text_mark_get_name (mark))); + g_assert (gtk_text_mark_get_buffer (mark) == NULL); + g_assert (gtk_text_mark_get_deleted (mark)); + g_assert (gtk_text_buffer_get_mark (buf1, "foo") == NULL); + + gtk_text_buffer_get_start_iter (buf2, &iter); + gtk_text_buffer_add_mark (buf2, mark, &iter); + gtk_text_buffer_insert (buf2, &iter, "ewfwefwefwe", -1); + gtk_text_buffer_get_iter_at_mark (buf2, &iter, mark); + + g_assert (gtk_text_mark_get_visible (mark)); + g_assert (gtk_text_iter_is_start (&iter)); + g_assert (gtk_text_mark_get_left_gravity (mark)); + g_assert (!strcmp ("foo", gtk_text_mark_get_name (mark))); + g_assert (gtk_text_mark_get_buffer (mark) == buf2); + g_assert (!gtk_text_mark_get_deleted (mark)); + g_assert (gtk_text_buffer_get_mark (buf2, "foo") == mark); + + gtk_text_buffer_delete_mark (buf2, mark); + gtk_text_mark_set_visible (mark, FALSE); + g_object_unref (mark); + + mark = gtk_text_mark_new ("blah", TRUE); + gtk_text_buffer_get_start_iter (buf1, &iter); + gtk_text_mark_set_visible (mark, TRUE); + gtk_text_buffer_add_mark (buf1, mark, &iter); + + g_assert (gtk_text_mark_get_visible (mark)); + g_assert (gtk_text_mark_get_buffer (mark) == buf1); + g_assert (!gtk_text_mark_get_deleted (mark)); + g_assert (gtk_text_buffer_get_mark (buf1, "blah") == mark); + g_assert (!strcmp ("blah", gtk_text_mark_get_name (mark))); + + gtk_text_mark_set_visible (mark, FALSE); + gtk_text_buffer_delete_mark (buf1, mark); + g_assert (!gtk_text_mark_get_visible (mark)); + g_assert (gtk_text_buffer_get_mark (buf1, "blah") == NULL); + g_assert (gtk_text_mark_get_buffer (mark) == NULL); + g_assert (gtk_text_mark_get_deleted (mark)); + + gtk_text_buffer_get_start_iter (buf2, &iter); + gtk_text_buffer_add_mark (buf2, mark, &iter); + g_assert (gtk_text_mark_get_buffer (mark) == buf2); + g_assert (!gtk_text_mark_get_deleted (mark)); + g_assert (gtk_text_buffer_get_mark (buf2, "blah") == mark); + g_assert (!strcmp ("blah", gtk_text_mark_get_name (mark))); + + g_object_unref (mark); + g_object_unref (buf1); + g_object_unref (buf2); +}