[gtksourceview/wip/marks-sequence] Implement private class GtkSourceMarksSequence



commit 58fc2417e32b28458eb47e1e05b62fde4b6e68a7
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Sun Sep 8 15:25:55 2013 +0200

    Implement private class GtkSourceMarksSequence
    
    It will be used for GtkSourceMark. And it is useful for the code folding
    too.
    
    More unit tests are needed.

 docs/reference/Makefile.am             |    1 +
 gtksourceview/Makefile.am              |    2 +
 gtksourceview/gtksourcemarkssequence.c |  576 ++++++++++++++++++++++++++++++++
 gtksourceview/gtksourcemarkssequence.h |   97 ++++++
 gtksourceview/gtksourcetypes-private.h |    1 +
 tests/Makefile.am                      |    7 +
 tests/test-marks-sequence.c            |   72 ++++
 7 files changed, 756 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 8acf0b3..1987d7f 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -36,6 +36,7 @@ IGNORE_HFILES =                                       \
        gtksourcegutterrenderermarks.h          \
        gtksourcegutterrenderer-private.h       \
        gtksourcelanguage-private.h             \
+       gtksourcemarkssequence.h                \
        gtksourcepixbufhelper.h                 \
        gtksourceregex.h                        \
        gtksourcestyle-private.h                \
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index 735bfb6..e27d220 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -61,6 +61,7 @@ libgtksourceview_private_headers = \
        gtksourcegutterrenderermarks.h          \
        gtksourcegutterrenderer-private.h       \
        gtksourcelanguage-private.h             \
+       gtksourcemarkssequence.h                \
        gtksourcepixbufhelper.h                 \
        gtksourceregex.h                        \
        gtksourcestyle-private.h                \
@@ -79,6 +80,7 @@ libgtksourceview_private_c_files = \
        gtksourcegutterrenderermarks.c  \
        gtksourcelanguage-parser-1.c    \
        gtksourcelanguage-parser-2.c    \
+       gtksourcemarkssequence.c        \
        gtksourcepixbufhelper.c         \
        gtksourceregex.c                \
        gtksourceundomanager.c          \
diff --git a/gtksourceview/gtksourcemarkssequence.c b/gtksourceview/gtksourcemarkssequence.c
new file mode 100644
index 0000000..b0df4e8
--- /dev/null
+++ b/gtksourceview/gtksourcemarkssequence.c
@@ -0,0 +1,576 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/* gtksourcemarkssequence.c
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2013 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "gtksourcemarkssequence.h"
+
+/* An object for storing GtkTextMarks. The text marks are sorted internally with
+ * a GSequence. Going to the previous or next text mark has a O(1) complexity.
+ * Finding a text mark in the neighborhood of a text iter has a O(log n)
+ * complexity, with 'n' the number of marks in the sequence.
+ *
+ * The GSequenceIter associated to a text mark is inserted into the text mark,
+ * with g_object_set_qdata(). So the text mark knows its position in the
+ * GSequence. This allows to use normal GtkTextMarks in the API, instead of
+ * using a subclass or a custom iter.
+ */
+
+enum
+{
+       PROP_0,
+       PROP_BUFFER,
+};
+
+struct _GtkSourceMarksSequencePrivate
+{
+       GtkTextBuffer *buffer;
+       GSequence *seq;
+
+       /* The quark used for storing the GSequenceIter into the text mark, with
+        * g_object_set_qdata().
+        */
+       GQuark quark;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceMarksSequence, _gtk_source_marks_sequence, G_TYPE_OBJECT)
+
+static void
+remove_qdata (GtkTextMark            *mark,
+             GtkSourceMarksSequence *seq)
+{
+       g_object_set_qdata (G_OBJECT (mark),
+                           seq->priv->quark,
+                           NULL);
+}
+
+static void
+free_sequence (GtkSourceMarksSequence *seq)
+{
+       if (seq->priv->seq != NULL)
+       {
+               g_sequence_foreach (seq->priv->seq,
+                                   (GFunc)remove_qdata,
+                                   seq);
+
+               g_sequence_free (seq->priv->seq);
+               seq->priv->seq = NULL;
+       }
+}
+
+static gint
+compare_marks (GtkTextMark *mark1,
+              GtkTextMark *mark2)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter iter1;
+       GtkTextIter iter2;
+
+       g_assert (GTK_IS_TEXT_MARK (mark1));
+       g_assert (GTK_IS_TEXT_MARK (mark2));
+
+       buffer = gtk_text_mark_get_buffer (mark1);
+
+       g_assert (buffer == gtk_text_mark_get_buffer (mark2));
+
+       gtk_text_buffer_get_iter_at_mark (buffer, &iter1, mark1);
+       gtk_text_buffer_get_iter_at_mark (buffer, &iter2, mark2);
+
+       return gtk_text_iter_compare (&iter1, &iter2);
+}
+
+static void
+mark_set_cb (GtkTextBuffer          *buffer,
+            GtkTextIter            *location,
+            GtkTextMark            *mark,
+            GtkSourceMarksSequence *seq)
+{
+       GSequenceIter *seq_iter;
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       if (seq_iter != NULL)
+       {
+               g_sequence_sort_changed (seq_iter,
+                                        (GCompareDataFunc)compare_marks,
+                                        NULL);
+       }
+}
+
+static void
+set_buffer (GtkSourceMarksSequence *seq,
+           GtkTextBuffer          *buffer)
+{
+       g_assert (seq->priv->buffer == NULL);
+
+       seq->priv->buffer = g_object_ref (buffer);
+
+       g_signal_connect_object (buffer,
+                                "mark-set",
+                                G_CALLBACK (mark_set_cb),
+                                seq,
+                                0);
+}
+
+static void
+_gtk_source_marks_sequence_dispose (GObject *object)
+{
+       GtkSourceMarksSequence *seq = GTK_SOURCE_MARKS_SEQUENCE (object);
+
+       g_clear_object (&seq->priv->buffer);
+
+       free_sequence (seq);
+
+       G_OBJECT_CLASS (_gtk_source_marks_sequence_parent_class)->dispose (object);
+}
+
+static void
+_gtk_source_marks_sequence_get_property (GObject    *object,
+                                        guint       prop_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec)
+{
+       GtkSourceMarksSequence *seq;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (object));
+
+       seq = GTK_SOURCE_MARKS_SEQUENCE (object);
+
+       switch (prop_id)
+       {
+               case PROP_BUFFER:
+                       g_value_set_object (value, seq->priv->buffer);
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
+       }
+}
+
+static void
+_gtk_source_marks_sequence_set_property (GObject      *object,
+                                        guint         prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+       GtkSourceMarksSequence *seq;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (object));
+
+       seq = GTK_SOURCE_MARKS_SEQUENCE (object);
+
+       switch (prop_id)
+       {
+               case PROP_BUFFER:
+                       set_buffer (seq, g_value_get_object (value));
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
+       }
+}
+
+static void
+_gtk_source_marks_sequence_class_init (GtkSourceMarksSequenceClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->dispose = _gtk_source_marks_sequence_dispose;
+       object_class->get_property = _gtk_source_marks_sequence_get_property;
+       object_class->set_property = _gtk_source_marks_sequence_set_property;
+
+       g_object_class_install_property (object_class,
+                                        PROP_BUFFER,
+                                        g_param_spec_object ("buffer",
+                                                             "Buffer",
+                                                             "The text buffer",
+                                                             GTK_TYPE_TEXT_BUFFER,
+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+_gtk_source_marks_sequence_init (GtkSourceMarksSequence *seq)
+{
+       gchar *unique_str;
+
+       seq->priv = _gtk_source_marks_sequence_get_instance_private (seq);
+
+       seq->priv->seq = g_sequence_new ((GDestroyNotify)g_object_unref);
+
+       unique_str = g_strdup_printf ("gtk-source-marks-sequence-%p", seq);
+       seq->priv->quark = g_quark_from_string (unique_str);
+       g_free (unique_str);
+}
+
+GtkSourceMarksSequence *
+_gtk_source_marks_sequence_new (GtkTextBuffer *buffer)
+{
+       g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+
+       return g_object_new (GTK_SOURCE_TYPE_MARKS_SEQUENCE,
+                            "buffer", buffer,
+                            NULL);
+}
+
+void
+_gtk_source_marks_sequence_add (GtkSourceMarksSequence *seq,
+                               GtkTextMark            *mark)
+{
+       GSequenceIter *seq_iter;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq));
+       g_return_if_fail (GTK_IS_TEXT_MARK (mark));
+       g_return_if_fail (gtk_text_mark_get_buffer (mark) == seq->priv->buffer);
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       if (seq_iter != NULL)
+       {
+               /* The mark is already added. */
+               return;
+       }
+
+       seq_iter = g_sequence_insert_sorted (seq->priv->seq,
+                                            mark,
+                                            (GCompareDataFunc)compare_marks,
+                                            NULL);
+
+       g_object_ref (mark);
+       g_object_set_qdata (G_OBJECT (mark),
+                           seq->priv->quark,
+                           seq_iter);
+}
+
+void
+_gtk_source_marks_sequence_remove (GtkSourceMarksSequence *seq,
+                                  GtkTextMark            *mark)
+{
+       GSequenceIter *seq_iter;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq));
+       g_return_if_fail (GTK_IS_TEXT_MARK (mark));
+       g_return_if_fail (gtk_text_mark_get_buffer (mark) == seq->priv->buffer);
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       if (seq_iter != NULL)
+       {
+               g_object_set_qdata (G_OBJECT (mark), seq->priv->quark, NULL);
+               g_sequence_remove (seq_iter);
+       }
+}
+
+GtkTextMark *
+_gtk_source_marks_sequence_next (GtkSourceMarksSequence *seq,
+                                GtkTextMark            *mark)
+{
+       GSequenceIter *seq_iter;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), NULL);
+       g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), NULL);
+       g_return_val_if_fail (gtk_text_mark_get_buffer (mark) == seq->priv->buffer, NULL);
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       g_return_val_if_fail (seq_iter != NULL, NULL);
+
+       seq_iter = g_sequence_iter_next (seq_iter);
+
+       return g_sequence_iter_is_end (seq_iter) ? NULL : g_sequence_get (seq_iter);
+}
+
+GtkTextMark *
+_gtk_source_marks_sequence_prev (GtkSourceMarksSequence *seq,
+                                GtkTextMark            *mark)
+{
+       GSequenceIter *seq_iter;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), NULL);
+       g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), NULL);
+       g_return_val_if_fail (gtk_text_mark_get_buffer (mark) == seq->priv->buffer, NULL);
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       g_return_val_if_fail (seq_iter != NULL, NULL);
+
+       if (g_sequence_iter_is_begin (seq_iter))
+       {
+               return NULL;
+       }
+
+       seq_iter = g_sequence_iter_prev (seq_iter);
+
+       return g_sequence_get (seq_iter);
+}
+
+/* Moves @iter forward to the next position where there is at least one mark.
+ * Returns %TRUE if @iter was moved.
+ */
+gboolean
+_gtk_source_marks_sequence_forward_iter (GtkSourceMarksSequence *seq,
+                                        GtkTextIter            *iter)
+{
+       GtkTextMark *mark;
+       GSequenceIter *seq_iter;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), FALSE);
+       g_return_val_if_fail (iter != NULL, FALSE);
+       g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == seq->priv->buffer, FALSE);
+
+       mark = gtk_text_buffer_create_mark (seq->priv->buffer,
+                                           NULL,
+                                           iter,
+                                           TRUE);
+
+       seq_iter = g_sequence_search (seq->priv->seq,
+                                     mark,
+                                     (GCompareDataFunc)compare_marks,
+                                     NULL);
+
+       gtk_text_buffer_delete_mark (seq->priv->buffer, mark);
+
+       while (!g_sequence_iter_is_end (seq_iter))
+       {
+               GtkTextMark *cur_mark = g_sequence_get (seq_iter);
+               GtkTextIter cur_iter;
+
+               gtk_text_buffer_get_iter_at_mark (seq->priv->buffer, &cur_iter, cur_mark);
+
+               if (gtk_text_iter_compare (iter, &cur_iter) < 0)
+               {
+                       *iter = cur_iter;
+                       return TRUE;
+               }
+
+               seq_iter = g_sequence_iter_next (seq_iter);
+       }
+
+       return FALSE;
+}
+
+/* Moves @iter backward to the previous position where there is at least one
+ * mark. Returns %TRUE if @iter was moved.
+ */
+gboolean
+_gtk_source_marks_sequence_backward_iter (GtkSourceMarksSequence *seq,
+                                         GtkTextIter            *iter)
+{
+       GtkTextMark *mark;
+       GSequenceIter *seq_iter;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), FALSE);
+       g_return_val_if_fail (iter != NULL, FALSE);
+       g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == seq->priv->buffer, FALSE);
+
+       mark = gtk_text_buffer_create_mark (seq->priv->buffer,
+                                           NULL,
+                                           iter,
+                                           TRUE);
+
+       seq_iter = g_sequence_search (seq->priv->seq,
+                                     mark,
+                                     (GCompareDataFunc)compare_marks,
+                                     NULL);
+
+       gtk_text_buffer_delete_mark (seq->priv->buffer, mark);
+
+       if (g_sequence_iter_is_end (seq_iter))
+       {
+               seq_iter = g_sequence_iter_prev (seq_iter);
+       }
+
+       if (g_sequence_iter_is_end (seq_iter))
+       {
+               /* The sequence is empty. */
+               return FALSE;
+       }
+
+       while (TRUE)
+       {
+               GtkTextMark *cur_mark;
+               GtkTextIter cur_iter;
+
+               cur_mark = g_sequence_get (seq_iter);
+
+               gtk_text_buffer_get_iter_at_mark (seq->priv->buffer, &cur_iter, cur_mark);
+
+               if (gtk_text_iter_compare (&cur_iter, iter) < 0)
+               {
+                       *iter = cur_iter;
+                       return TRUE;
+               }
+
+               if (g_sequence_iter_is_begin (seq_iter))
+               {
+                       break;
+               }
+
+               seq_iter = g_sequence_iter_prev (seq_iter);
+       }
+
+       return FALSE;
+}
+
+static GSList *
+get_marks_in_range (GtkSourceMarksSequence *seq,
+                   const GtkTextIter      *iter1,
+                   const GtkTextIter      *iter2)
+{
+       GtkTextIter start;
+       GtkTextIter end;
+       GtkTextMark *mark_start;
+       GSequenceIter *seq_iter;
+       GSequenceIter *first_seq_iter = NULL;
+       GSList *ret = NULL;
+
+       start = *iter1;
+       end = *iter2;
+
+       gtk_text_iter_order (&start, &end);
+
+       mark_start = gtk_text_buffer_create_mark (seq->priv->buffer,
+                                                 NULL,
+                                                 &start,
+                                                 TRUE);
+
+       seq_iter = g_sequence_search (seq->priv->seq,
+                                     mark_start,
+                                     (GCompareDataFunc)compare_marks,
+                                     NULL);
+
+       gtk_text_buffer_delete_mark (seq->priv->buffer, mark_start);
+
+       if (g_sequence_iter_is_end (seq_iter))
+       {
+               seq_iter = g_sequence_iter_prev (seq_iter);
+       }
+
+       if (g_sequence_iter_is_end (seq_iter))
+       {
+               /* The sequence is empty. */
+               return NULL;
+       }
+
+       /* Find the first mark */
+
+       while (TRUE)
+       {
+               GtkTextMark *cur_mark;
+               GtkTextIter cur_iter;
+
+               cur_mark = g_sequence_get (seq_iter);
+               gtk_text_buffer_get_iter_at_mark (seq->priv->buffer, &cur_iter, cur_mark);
+
+               if (gtk_text_iter_compare (&cur_iter, &start) < 0)
+               {
+                       break;
+               }
+
+               first_seq_iter = seq_iter;
+
+               if (g_sequence_iter_is_begin (seq_iter))
+               {
+                       break;
+               }
+
+               seq_iter = g_sequence_iter_prev (seq_iter);
+       }
+
+       if (first_seq_iter == NULL)
+       {
+               /* The last mark in the sequence is before @start. */
+               return NULL;
+       }
+
+       /* Go forward until @end to fill the list of marks */
+
+       for (seq_iter = first_seq_iter;
+            !g_sequence_iter_is_end (seq_iter);
+            seq_iter = g_sequence_iter_next (seq_iter))
+       {
+               GtkTextMark *cur_mark;
+               GtkTextIter cur_iter;
+
+               cur_mark = g_sequence_get (seq_iter);
+               gtk_text_buffer_get_iter_at_mark (seq->priv->buffer, &cur_iter, cur_mark);
+
+               if (gtk_text_iter_compare (&end, &cur_iter) < 0)
+               {
+                       break;
+               }
+
+               ret = g_slist_prepend (ret, cur_mark);
+       }
+
+       return ret;
+}
+
+GSList *
+_gtk_source_marks_sequence_get_marks_at_iter (GtkSourceMarksSequence *seq,
+                                             const GtkTextIter      *iter)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), NULL);
+       g_return_val_if_fail (iter != NULL, NULL);
+       g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == seq->priv->buffer, NULL);
+
+       return get_marks_in_range (seq, iter, iter);
+}
+
+GSList *
+_gtk_source_marks_sequence_get_marks_at_line (GtkSourceMarksSequence *seq,
+                                             gint                    line)
+{
+       GtkTextIter start;
+       GtkTextIter end;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), NULL);
+
+       gtk_text_buffer_get_iter_at_line (seq->priv->buffer, &start, line);
+
+       end = start;
+       gtk_text_iter_forward_to_line_end (&end);
+
+       return get_marks_in_range (seq, &start, &end);
+}
+
+void
+_gtk_source_marks_sequence_remove_in_range (GtkSourceMarksSequence *seq,
+                                           const GtkTextIter      *start,
+                                           const GtkTextIter      *end)
+{
+       GSList *marks;
+       GSList *l;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq));
+       g_return_if_fail (start != NULL);
+       g_return_if_fail (gtk_text_iter_get_buffer (start) == seq->priv->buffer);
+       g_return_if_fail (end != NULL);
+       g_return_if_fail (gtk_text_iter_get_buffer (end) == seq->priv->buffer);
+
+       marks = get_marks_in_range (seq, start, end);
+
+       for (l = marks; l != NULL; l = l->next)
+       {
+               _gtk_source_marks_sequence_remove (seq, l->data);
+       }
+
+       g_slist_free (marks);
+}
diff --git a/gtksourceview/gtksourcemarkssequence.h b/gtksourceview/gtksourcemarkssequence.h
new file mode 100644
index 0000000..79539fe
--- /dev/null
+++ b/gtksourceview/gtksourcemarkssequence.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/* gtksourcemarkssequence.h
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2013 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_MARKS_SEQUENCE_H__
+#define __GTK_SOURCE_MARKS_SEQUENCE_H__
+
+#include <gtk/gtk.h>
+#include "gtksourcetypes-private.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_MARKS_SEQUENCE             (_gtk_source_marks_sequence_get_type ())
+#define GTK_SOURCE_MARKS_SEQUENCE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE, GtkSourceMarksSequence))
+#define GTK_SOURCE_MARKS_SEQUENCE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE, GtkSourceMarksSequenceClass))
+#define GTK_SOURCE_IS_MARKS_SEQUENCE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE))
+#define GTK_SOURCE_IS_MARKS_SEQUENCE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE))
+#define GTK_SOURCE_MARKS_SEQUENCE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE, GtkSourceMarksSequenceClass))
+
+typedef struct _GtkSourceMarksSequenceClass    GtkSourceMarksSequenceClass;
+typedef struct _GtkSourceMarksSequencePrivate  GtkSourceMarksSequencePrivate;
+
+struct _GtkSourceMarksSequence
+{
+       GObject parent;
+
+       GtkSourceMarksSequencePrivate *priv;
+};
+
+struct _GtkSourceMarksSequenceClass
+{
+       GObjectClass parent_class;
+};
+
+G_GNUC_INTERNAL
+GType                   _gtk_source_marks_sequence_get_type            (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GtkSourceMarksSequence *_gtk_source_marks_sequence_new                 (GtkTextBuffer          *buffer);
+
+G_GNUC_INTERNAL
+void                    _gtk_source_marks_sequence_add                 (GtkSourceMarksSequence *seq,
+                                                                        GtkTextMark            *mark);
+
+G_GNUC_INTERNAL
+void                    _gtk_source_marks_sequence_remove              (GtkSourceMarksSequence *seq,
+                                                                        GtkTextMark            *mark);
+
+G_GNUC_INTERNAL
+GtkTextMark            *_gtk_source_marks_sequence_next                (GtkSourceMarksSequence *seq,
+                                                                        GtkTextMark            *mark);
+
+G_GNUC_INTERNAL
+GtkTextMark            *_gtk_source_marks_sequence_prev                (GtkSourceMarksSequence *seq,
+                                                                        GtkTextMark            *mark);
+
+G_GNUC_INTERNAL
+gboolean                _gtk_source_marks_sequence_forward_iter        (GtkSourceMarksSequence *seq,
+                                                                        GtkTextIter            *iter);
+
+G_GNUC_INTERNAL
+gboolean                _gtk_source_marks_sequence_backward_iter       (GtkSourceMarksSequence *seq,
+                                                                        GtkTextIter            *iter);
+
+G_GNUC_INTERNAL
+GSList                 *_gtk_source_marks_sequence_get_marks_at_iter   (GtkSourceMarksSequence *seq,
+                                                                        const GtkTextIter      *iter);
+
+G_GNUC_INTERNAL
+GSList                 *_gtk_source_marks_sequence_get_marks_at_line   (GtkSourceMarksSequence *seq,
+                                                                        gint                    line);
+
+G_GNUC_INTERNAL
+void                    _gtk_source_marks_sequence_remove_in_range     (GtkSourceMarksSequence *seq,
+                                                                        const GtkTextIter      *start,
+                                                                        const GtkTextIter      *end);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_MARKS_SEQUENCE_H__ */
diff --git a/gtksourceview/gtksourcetypes-private.h b/gtksourceview/gtksourcetypes-private.h
index 6b88c2c..afb1c8a 100644
--- a/gtksourceview/gtksourcetypes-private.h
+++ b/gtksourceview/gtksourcetypes-private.h
@@ -32,6 +32,7 @@ typedef struct _GtkSourceContextEngine                GtkSourceContextEngine;
 typedef struct _GtkSourceEngine                        GtkSourceEngine;
 typedef struct _GtkSourceGutterRendererLines   GtkSourceGutterRendererLines;
 typedef struct _GtkSourceGutterRendererMarks   GtkSourceGutterRendererMarks;
+typedef struct _GtkSourceMarksSequence         GtkSourceMarksSequence;
 typedef struct _GtkSourcePixbufHelper          GtkSourcePixbufHelper;
 typedef struct _GtkSourceRegex                 GtkSourceRegex;
 typedef struct _GtkSourceUndoManagerDefault    GtkSourceUndoManagerDefault;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b5e6827..0dbcc93 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -102,6 +102,13 @@ test_mark_LDADD =          \
        $(DEP_LIBS)                     \
        $(TESTS_LIBS)
 
+UNIT_TEST_PROGS += test-marks-sequence
+test_marks_sequence_SOURCES = test-marks-sequence.c
+test_marks_sequence_LDADD =                                            \
+       $(top_builddir)/gtksourceview/libgtksourceview-private.la       \
+       $(DEP_LIBS)                                                     \
+       $(TESTS_LIBS)
+
 UNIT_TEST_PROGS += test-printcompositor
 test_printcompositor_SOURCES =         \
        test-printcompositor.c
diff --git a/tests/test-marks-sequence.c b/tests/test-marks-sequence.c
new file mode 100644
index 0000000..de27d6d
--- /dev/null
+++ b/tests/test-marks-sequence.c
@@ -0,0 +1,72 @@
+/*
+ * test-marks-sequence.c
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2013 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <gtk/gtk.h>
+#include "gtksourceview/gtksourcemarkssequence.h"
+
+static void
+test_simple (void)
+{
+       GtkTextBuffer *buffer = gtk_text_buffer_new (NULL);
+       GtkSourceMarksSequence *seq = _gtk_source_marks_sequence_new (buffer);
+       GtkTextIter iter;
+       GtkTextMark *mark1;
+       GtkTextMark *mark2;
+       GtkTextMark *mark;
+
+       gtk_text_buffer_set_text (buffer, "text", -1);
+
+       gtk_text_buffer_get_start_iter (buffer, &iter);
+       mark1 = gtk_text_buffer_create_mark (buffer, NULL, &iter, TRUE);
+
+       gtk_text_buffer_get_end_iter (buffer, &iter);
+       mark2 = gtk_text_buffer_create_mark (buffer, NULL, &iter, TRUE);
+
+       _gtk_source_marks_sequence_add (seq, mark1);
+       _gtk_source_marks_sequence_add (seq, mark2);
+
+       mark = _gtk_source_marks_sequence_next (seq, mark1);
+       g_assert (mark == mark2);
+
+       mark = _gtk_source_marks_sequence_next (seq, mark2);
+       g_assert (mark == NULL);
+
+       mark = _gtk_source_marks_sequence_prev (seq, mark2);
+       g_assert (mark == mark1);
+
+       mark = _gtk_source_marks_sequence_prev (seq, mark1);
+       g_assert (mark == NULL);
+
+       _gtk_source_marks_sequence_remove (seq, mark2);
+
+       g_object_unref (buffer);
+       g_object_unref (seq);
+}
+
+int
+main (int argc, char **argv)
+{
+       gtk_test_init (&argc, &argv);
+
+       g_test_add_func ("/MarksSequence/simple", test_simple);
+
+       return g_test_run ();
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]