X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktextmark.c;h=33b0649a6f66f050b9e73b03b07ec16bb38b4efc;hb=c6fbdb67f3dc86cf49f2ac5ac8c072f4e586fd29;hp=4e20eb300c6a4898f26fd60ad352a502155c6cd5;hpb=f1de9df0511db6e82ee12c8e7f78d68d7a31cbbe;p=~andy%2Fgtk diff --git a/gtk/gtktextmark.c b/gtk/gtktextmark.c index 4e20eb300..33b0649a6 100644 --- a/gtk/gtktextmark.c +++ b/gtk/gtktextmark.c @@ -47,58 +47,100 @@ * */ - +#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API +#include "config.h" #include "gtktextbtree.h" +#include "gtkprivate.h" +#include "gtkintl.h" -static void gtk_text_mark_init (GtkTextMark *mark); -static void gtk_text_mark_class_init (GtkTextMarkClass *klass); -static void gtk_text_mark_finalize (GObject *obj); +/** + * SECTION:gtktextmark + * @Short_description: A position in the buffer preserved across buffer modifications + * @Title: 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. + * + * A #GtkTextMark is like a bookmark in a text buffer; it preserves a position in + * the text. You can convert the mark to an iterator using + * gtk_text_buffer_get_iter_at_mark(). Unlike iterators, marks remain valid across + * buffer mutations, because their behavior is defined when text is inserted or + * deleted. When text containing a mark is deleted, the mark remains in the + * position originally occupied by the deleted text. When text is inserted at a + * mark, a mark with left gravity will be moved to the + * beginning of the newly-inserted text, and a mark with right + * gravity will be moved to the end. + * + * + * "left" and "right" here refer to logical direction (left is the toward the start + * of the buffer); in some languages such as Hebrew the logically-leftmost text is + * not actually on the left when displayed. + * + * + * Marks are reference counted, but the reference count only controls the validity + * of the memory; marks can be deleted from the buffer at any time with + * gtk_text_buffer_delete_mark(). Once deleted from the buffer, a mark is + * essentially useless. + * + * Marks optionally have names; these can be convenient to avoid passing the + * #GtkTextMark object around. + * + * Marks are typically created using the gtk_text_buffer_create_mark() function. + */ -static gpointer parent_class = NULL; -GType -gtk_text_mark_get_type (void) -{ - static GType object_type = 0; +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); - if (!object_type) - { - static const GTypeInfo object_info = - { - sizeof (GtkTextMarkClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gtk_text_mark_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkTextMark), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_text_mark_init, - }; - - object_type = g_type_register_static (G_TYPE_OBJECT, - "GtkTextMark", - &object_info, 0); - } +static GtkTextLineSegment *gtk_mark_segment_new (GtkTextMark *mark_obj); - return object_type; -} +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) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - parent_class = g_type_class_peek_parent (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 @@ -113,8 +155,6 @@ gtk_text_mark_finalize (GObject *obj) if (seg) { - g_return_if_fail (seg->body.mark.tree == NULL); - if (seg->body.mark.tree != NULL) g_warning ("GtkTextMark being finalized while still in the buffer; " "someone removed a reference they didn't own! Crash " @@ -125,6 +165,91 @@ gtk_text_mark_finalize (GObject *obj) mark->segment = NULL; } + + /* chain parent_class' handler */ + 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: (allow-none): 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); } /** @@ -132,7 +257,7 @@ gtk_text_mark_finalize (GObject *obj) * @mark: a #GtkTextMark * * Returns %TRUE if the mark is visible (i.e. a cursor is displayed - * for it) + * for it). * * Return value: %TRUE if visible **/ @@ -169,8 +294,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 **/ @@ -179,7 +304,7 @@ gtk_text_mark_get_deleted (GtkTextMark *mark) { GtkTextLineSegment *seg; - g_return_val_if_fail (mark != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), FALSE); seg = mark->segment; @@ -189,6 +314,50 @@ gtk_text_mark_get_deleted (GtkTextMark *mark) return seg->body.mark.tree == NULL; } +/** + * gtk_text_mark_get_buffer: + * @mark: a #GtkTextMark + * + * Gets the buffer this mark is located inside, + * or %NULL if the mark is deleted. + * + * Return value: (transfer none): the mark's #GtkTextBuffer + **/ +GtkTextBuffer* +gtk_text_mark_get_buffer (GtkTextMark *mark) +{ + GtkTextLineSegment *seg; + + g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), NULL); + + seg = mark->segment; + + if (seg->body.mark.tree == NULL) + return NULL; + else + return _gtk_text_btree_get_buffer (seg->body.mark.tree); +} + +/** + * gtk_text_mark_get_left_gravity: + * @mark: a #GtkTextMark + * + * Determines whether the mark has left gravity. + * + * Return value: %TRUE if the mark has left gravity, %FALSE otherwise + **/ +gboolean +gtk_text_mark_get_left_gravity (GtkTextMark *mark) +{ + GtkTextLineSegment *seg; + + g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), FALSE); + + seg = mark->segment; + + return seg->type == >k_text_left_mark_type; +} + /* * Macro that determines the size of a mark segment: */ @@ -197,28 +366,22 @@ gtk_text_mark_get_deleted (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; @@ -228,6 +391,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); @@ -244,7 +424,7 @@ static void mark_segment_check_func (GtkTextLineSegment *segPtr * their gravity property. */ -GtkTextLineSegmentClass gtk_text_right_mark_type = { +const GtkTextLineSegmentClass gtk_text_right_mark_type = { "mark", /* name */ FALSE, /* leftGravity */ NULL, /* splitFunc */ @@ -254,7 +434,7 @@ GtkTextLineSegmentClass gtk_text_right_mark_type = { mark_segment_check_func /* checkFunc */ }; -GtkTextLineSegmentClass gtk_text_left_mark_type = { +const GtkTextLineSegmentClass gtk_text_left_mark_type = { "mark", /* name */ TRUE, /* leftGravity */ NULL, /* splitFunc */ @@ -273,21 +453,27 @@ GtkTextLineSegmentClass gtk_text_left_mark_type = { * a mark lies in a range of characters being deleted. * * Results: - * Returns 1 to indicate that deletion has been rejected. + * Returns 1 to indicate that deletion has been rejected, + * or 0 otherwise * * Side effects: - * None (even if the whole tree is being deleted we don't - * free up the mark; it will be done elsewhere). + * Frees mark if tree is going away * *-------------------------------------------------------------- */ static gboolean -mark_segment_delete_func (GtkTextLineSegment *segPtr, +mark_segment_delete_func (GtkTextLineSegment *seg, GtkTextLine *line, gboolean tree_gone) { - return TRUE; + if (tree_gone) + { + _gtk_text_btree_release_mark_segment (seg->body.mark.tree, seg); + return FALSE; + } + else + return TRUE; } /*