Allow to separate GtkTextMark creation from buffer insertion. (#132818,

2007-05-25  Matthias Clasen  <mclasen@redhat.com>

        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
This commit is contained in:
Matthias Clasen 2007-05-26 04:10:42 +00:00 committed by Matthias Clasen
parent a7f95266a5
commit 6ac30db004
11 changed files with 317 additions and 35 deletions

View File

@ -1,3 +1,22 @@
2007-05-25 Matthias Clasen <mclasen@redhat.com>
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 <xan@gnome.org>
* gtk/gtkaction.c: (gtk_action_set_short_label):

View File

@ -1,3 +1,7 @@
2007-05-25 Matthias Clasen <mclasen@redhat.com>
* gtk/gtk-sections.txt: Add new functions
2007-05-24 Matthias Clasen <mclasen@redhat.com>
* === Released 2.11.0 ===

View File

@ -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
<FILE>gtktextmark</FILE>
<TITLE>GtkTextMark</TITLE>
GtkTextMark
gtk_text_mark_new
gtk_text_mark_set_visible
gtk_text_mark_get_visible
gtk_text_mark_get_deleted

View File

@ -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

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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,

View File

@ -50,18 +50,29 @@
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
#include <config.h>
#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 = &gtk_text_left_mark_type;
else
seg->type = &gtk_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 = &gtk_text_left_mark_type;
else
mark->type = &gtk_text_right_mark_type;
mark->body.mark.name = NULL;
mark->type = &gtk_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);

View File

@ -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);

View File

@ -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

View File

@ -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);
}