]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktextmark.c
label: Fix memleak
[~andy/gtk] / gtk / gtktextmark.c
index f4dae7c9962f986dab5ed98eb3e773f401153d7c..33b0649a6f66f050b9e73b03b07ec16bb38b4efc 100644 (file)
  *
  */
 
-
+#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 <link linkend="TextWidget">text widget
+ * conceptual overview</link> 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 <firstterm>left gravity</firstterm> will be moved to the
+ * beginning of the newly-inserted text, and a mark with <firstterm>right
+ * gravity</firstterm> will be moved to the end.
+ *
+ * <footnote>
+ * "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.
+ * </footnote>
+ *
+ * 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 = &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: (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
  **/
@@ -194,16 +319,16 @@ gtk_text_mark_get_deleted (GtkTextMark *mark)
  * @mark: a #GtkTextMark
  * 
  * Gets the buffer this mark is located inside,
- * or NULL if the mark is deleted.
- * 
- * Return value: the mark's #GtkTextBuffer
+ * 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), FALSE);
+  g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), NULL);
 
   seg = mark->segment;
 
@@ -213,6 +338,26 @@ gtk_text_mark_get_buffer (GtkTextMark *mark)
     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 == &gtk_text_left_mark_type;
+}
+
 /*
  * Macro that determines the size of a mark segment:
  */
@@ -221,28 +366,22 @@ gtk_text_mark_get_buffer (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;
 
@@ -252,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);
@@ -268,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 */
@@ -278,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 */
@@ -297,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;
 }
 
 /*