[gtksourceview/wip/chergert/gsv-gtk4: 70/117] gutter: add GtkSourceGutterLines



commit 6c87e5972d66d1b2b5c6cc092f08a8d51afdbacc
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jan 15 13:47:45 2020 -0800

    gutter: add GtkSourceGutterLines
    
    This new object provides an abstraction over tracking line information
    while working with gutters. In particular, it can help avoid many costly
    iterations over the text B-Tree.

 docs/reference/meson.build                   |   1 +
 gtksourceview/gtksource.h                    |   1 +
 gtksourceview/gtksourcegutterlines-private.h |  38 ++
 gtksourceview/gtksourcegutterlines.c         | 657 +++++++++++++++++++++++++++
 gtksourceview/gtksourcegutterlines.h         |  96 ++++
 gtksourceview/gtksourcetypes.h               |   1 +
 gtksourceview/meson.build                    |   2 +
 gtksourceview/quarkset-inline.h              | 185 ++++++++
 8 files changed, 981 insertions(+)
---
diff --git a/docs/reference/meson.build b/docs/reference/meson.build
index 26cd21b3..622a4de8 100644
--- a/docs/reference/meson.build
+++ b/docs/reference/meson.build
@@ -40,6 +40,7 @@ reference_private_h = [
   'gtksourcestyleschememanager-private.h',
   'gtksourcetypes-private.h',
   'gtksourceutils-private.h',
+  'quarkset-inline.h',
 ]
 
 reference_content_files = files([
diff --git a/gtksourceview/gtksource.h b/gtksourceview/gtksource.h
index 2f795750..517c06ae 100644
--- a/gtksourceview/gtksource.h
+++ b/gtksourceview/gtksource.h
@@ -39,6 +39,7 @@
 #include "gtksourceinit.h"
 #include "gtksourcelanguage.h"
 #include "gtksourcelanguagemanager.h"
+#include "gtksourcegutterlines.h"
 #include "gtksourcemap.h"
 #include "gtksourcemark.h"
 #include "gtksourcemarkattributes.h"
diff --git a/gtksourceview/gtksourcegutterlines-private.h b/gtksourceview/gtksourcegutterlines-private.h
new file mode 100644
index 00000000..6852218d
--- /dev/null
+++ b/gtksourceview/gtksourcegutterlines-private.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- /
+ *
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2019 - Christian Hergert <chergert redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "gtksourcegutterlines.h"
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL
+GtkSourceGutterLines *_gtk_source_gutter_lines_new             (GtkTextView          *text_view,
+                                                                const GtkTextIter    *begin,
+                                                                const GtkTextIter    *end,
+                                                                gboolean              needs_wrap_first,
+                                                                gboolean              needs_wrap_last);
+G_GNUC_INTERNAL
+guint                 _gtk_source_gutter_lines_get_cursor_line (GtkSourceGutterLines *lines);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcegutterlines.c b/gtksourceview/gtksourcegutterlines.c
new file mode 100644
index 00000000..718bae2c
--- /dev/null
+++ b/gtksourceview/gtksourcegutterlines.c
@@ -0,0 +1,657 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- /
+ *
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2019 - Christian Hergert <chergert redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtksourcegutterlines.h"
+#include "gtksourcegutterlines-private.h"
+
+#include "quarkset-inline.h"
+
+/**
+ * SECTION:gutterlines
+ * @title: GtkSourceGutterLines
+ * @short_description: Collected information about visible lines
+ *
+ * The #GtkSourceGutterLines object is used to collect information about
+ * visible lines.
+ *
+ * Use this from your #GtkSourceGutterRenderer::query-data to collect the
+ * necessary information on visible lines. Doing so reduces the number of
+ * passes through the text btree allowing GtkSourceView to reach more
+ * frames-per-second while performing kinetic scrolling.
+ *
+ * Since: 5.0
+ */
+
+struct _GtkSourceGutterLines
+{
+       GObject       parent_instance;
+       GtkTextView  *view;
+       GArray       *lines;
+       GdkRectangle  visible_rect;
+       guint         first;
+       guint         last;
+       guint         cursor_line;
+};
+
+typedef struct
+{
+       QuarkSet classes;
+       gint     y;
+       gint     height;
+       gint     first_height;
+       gint     last_height;
+} LineInfo;
+
+G_DEFINE_TYPE (GtkSourceGutterLines, gtk_source_gutter_lines, G_TYPE_OBJECT)
+
+static GQuark q_cursor_line;
+static GQuark q_prelit;
+static GQuark q_selected;
+
+static void
+gtk_source_gutter_lines_finalize (GObject *object)
+{
+       GtkSourceGutterLines *lines = (GtkSourceGutterLines *)object;
+
+       g_clear_pointer (&lines->lines, g_array_unref);
+       g_clear_object (&lines->view);
+
+       G_OBJECT_CLASS (gtk_source_gutter_lines_parent_class)->finalize (object);
+}
+
+static void
+gtk_source_gutter_lines_class_init (GtkSourceGutterLinesClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = gtk_source_gutter_lines_finalize;
+
+       q_cursor_line = g_quark_from_static_string ("cursor-line");
+       q_prelit = g_quark_from_static_string ("prelit");
+       q_selected = g_quark_from_static_string ("selected");
+}
+
+static void
+gtk_source_gutter_lines_init (GtkSourceGutterLines *self)
+{
+       self->cursor_line = -1;
+}
+
+static void
+clear_line_info (gpointer data)
+{
+       LineInfo *info = data;
+
+       info->y = 0;
+       info->height = 0;
+       quark_set_clear (&info->classes);
+}
+
+GtkSourceGutterLines *
+_gtk_source_gutter_lines_new (GtkTextView       *text_view,
+                              const GtkTextIter *begin,
+                              const GtkTextIter *end,
+                              gboolean           needs_wrap_first,
+                              gboolean           needs_wrap_last)
+{
+       GtkSourceGutterLines *lines;
+       GtkTextBuffer *buffer;
+       GtkTextMark *mark;
+       GtkTextIter iter;
+       gboolean single_line;
+       guint cursor_line;
+       guint i;
+
+       g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
+       g_return_val_if_fail (begin != NULL, NULL);
+       g_return_val_if_fail (end != NULL, NULL);
+       g_return_val_if_fail (begin != end, NULL);
+
+       buffer = gtk_text_view_get_buffer (text_view);
+
+       g_return_val_if_fail (gtk_text_iter_get_buffer (begin) == buffer, NULL);
+       g_return_val_if_fail (gtk_text_iter_get_buffer (end) == buffer, NULL);
+
+       if (gtk_text_iter_compare (begin, end) > 0)
+       {
+               const GtkTextIter *tmp = begin;
+               begin = end;
+               end = tmp;
+       }
+
+       g_return_val_if_fail (begin != end, NULL);
+
+       lines = g_object_new (GTK_SOURCE_TYPE_GUTTER_LINES, NULL);
+       lines->view = g_object_ref (text_view);
+       lines->first = gtk_text_iter_get_line (begin);
+       lines->last = gtk_text_iter_get_line (end);
+       lines->lines = g_array_sized_new (FALSE,
+                                         FALSE,
+                                         sizeof (LineInfo),
+                                         lines->last - lines->first + 1);
+       g_array_set_clear_func (lines->lines, clear_line_info);
+       gtk_text_view_get_visible_rect (text_view, &lines->visible_rect);
+
+       /* No need to calculate special wrapping if wrap mode is none */
+       if (gtk_text_view_get_wrap_mode (text_view) == GTK_WRAP_NONE)
+       {
+               needs_wrap_first = FALSE;
+               needs_wrap_last = FALSE;
+       }
+
+       single_line = !needs_wrap_first && !needs_wrap_last;
+
+       /* Get the line number containing the cursor to compare while
+        * building the lines to add the "cursor-line" quark.
+        */
+       mark = gtk_text_buffer_get_insert (buffer);
+       gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+       cursor_line = gtk_text_iter_get_line (&iter);
+
+       lines->cursor_line = cursor_line;
+
+       iter = *begin;
+
+       if (!gtk_text_iter_starts_line (&iter))
+       {
+               gtk_text_iter_set_line_offset (&iter, 0);
+       }
+
+       for (i = lines->first; i <= lines->last; i++)
+       {
+               LineInfo info = {0};
+
+               if G_LIKELY (single_line)
+               {
+                       GdkRectangle rect;
+
+                       gtk_text_view_get_iter_location (text_view, &iter, &rect);
+
+                       info.y = rect.y;
+                       info.height = rect.height;
+                       info.first_height = rect.height;
+                       info.last_height = rect.height;
+               }
+               else
+               {
+                       gtk_text_view_get_line_yrange (text_view,
+                                                      &iter,
+                                                      &info.y,
+                                                      &info.height);
+
+                       if (gtk_text_iter_starts_line (&iter) &&
+                           gtk_text_iter_ends_line (&iter))
+                       {
+                               info.first_height = info.height;
+                               info.last_height = info.height;
+                       }
+                       else
+                       {
+                               GdkRectangle rect;
+
+                               if (needs_wrap_first)
+                               {
+
+                                       gtk_text_view_get_iter_location (text_view,
+                                                                        &iter,
+                                                                        &rect);
+                                       info.first_height = rect.height;
+                               }
+                               else
+                               {
+                                       info.first_height = info.height;
+                               }
+
+                               if (needs_wrap_last)
+                               {
+                                       gtk_text_iter_forward_to_line_end (&iter);
+                                       gtk_text_view_get_iter_location (text_view,
+                                                                        &iter,
+                                                                        &rect);
+                                       info.last_height = rect.height;
+                               }
+                               else
+                               {
+                                       info.last_height = info.height;
+                               }
+                       }
+               }
+
+               if G_UNLIKELY (i == cursor_line)
+               {
+                       quark_set_add (&info.classes, q_cursor_line);
+               }
+
+               g_array_append_val (lines->lines, info);
+
+               if G_UNLIKELY (!gtk_text_iter_forward_line (&iter) &&
+                              !gtk_text_iter_is_end (&iter))
+               {
+                       break;
+               }
+       }
+
+       g_return_val_if_fail (lines->lines->len > 0, NULL);
+       g_return_val_if_fail ((lines->last - lines->first) >= (lines->lines->len - 1), NULL);
+
+       return g_steal_pointer (&lines);
+}
+
+/**
+ * gtk_source_gutter_lines_add_qclass:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ * @qname: a class name as a #GQuark
+ *
+ * Adds the class denoted by @qname to @line.
+ *
+ * You may check if a line has @qname by calling
+ * gtk_source_gutter_lines_has_qclass().
+ *
+ * You can remove @qname by calling
+ * gtk_source_gutter_lines_remove_qclass().
+ *
+ * Since: 5.0
+ */
+void
+gtk_source_gutter_lines_add_qclass (GtkSourceGutterLines *lines,
+                                    guint                 line,
+                                    GQuark                qname)
+{
+       LineInfo *info;
+
+       g_return_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines));
+       g_return_if_fail (qname != 0);
+       g_return_if_fail (line >= lines->first);
+       g_return_if_fail (line <= lines->last);
+       g_return_if_fail (line - lines->first < lines->lines->len);
+
+       info = &g_array_index (lines->lines, LineInfo, line - lines->first);
+       quark_set_add (&info->classes, qname);
+}
+
+/**
+ * gtk_source_gutter_lines_add_class:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ * @name: a class name
+ *
+ * Adds the class @name to @line.
+ *
+ * @name will be converted to a #GQuark as part of this process. A
+ * faster version of this function is available via
+ * gtk_source_gutter_lines_add_qclass() for situations where the #GQuark is
+ * known ahead of time.
+ *
+ * Since: 5.0
+ */
+void
+gtk_source_gutter_lines_add_class (GtkSourceGutterLines *lines,
+                                   guint                 line,
+                                   const gchar          *name)
+{
+       g_return_if_fail (name != NULL);
+
+       gtk_source_gutter_lines_add_qclass (lines,
+                                    line,
+                                    g_quark_from_string (name));
+}
+
+/**
+ * gtk_source_gutter_lines_remove_class:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ * @name: a class name
+ *
+ * Removes the class matching @name from @line.
+ *
+ * A faster version of this function is available via
+ * gtk_source_gutter_lines_remove_qclass() for situations where the
+ * #GQuark is known ahead of time.
+ *
+ * Since: 5.0
+ */
+void
+gtk_source_gutter_lines_remove_class (GtkSourceGutterLines *lines,
+                                      guint                 line,
+                                      const gchar          *name)
+{
+       GQuark qname;
+
+       g_return_if_fail (name != NULL);
+
+       qname = g_quark_try_string (name);
+
+       if (qname != 0)
+       {
+               gtk_source_gutter_lines_remove_qclass (lines, line, qname);
+       }
+}
+
+/**
+ * gtk_source_gutter_lines_remove_qclass:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ * @qname: a #GQuark to remove from @line
+ *
+ * Reverses a call to gtk_source_gutter_lines_add_qclass() by removing
+ * the #GQuark matching @qname.
+ *
+ * Since: 5.0
+ */
+void
+gtk_source_gutter_lines_remove_qclass (GtkSourceGutterLines *lines,
+                                       guint                 line,
+                                       GQuark                qname)
+{
+       LineInfo *info;
+
+       g_return_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines));
+       g_return_if_fail (qname != 0);
+       g_return_if_fail (line >= lines->first);
+       g_return_if_fail (line <= lines->last);
+       g_return_if_fail (line - lines->first < lines->lines->len);
+
+       info = &g_array_index (lines->lines, LineInfo, line - lines->first);
+       quark_set_remove (&info->classes, qname);
+}
+
+/**
+ * gtk_source_gutter_lines_has_class:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ * @name: a class name that may be convered to a #GQuark
+ *
+ * Checks to see if gtk_source_gutter_lines_add_class() was called with
+ * the @name for @line.
+ *
+ * A faster version of this function is provided via
+ * gtk_source_gutter_lines_has_qclass() for situations where the quark
+ * is known ahead of time.
+ *
+ * Returns: %TRUE if @line contains @name
+ *
+ * Since: 5.0
+ */
+gboolean
+gtk_source_gutter_lines_has_class (GtkSourceGutterLines *lines,
+                                   guint                 line,
+                                   const gchar          *name)
+{
+       GQuark qname;
+
+       g_return_val_if_fail (name != NULL, FALSE);
+
+       qname = g_quark_try_string (name);
+
+       if (qname == 0)
+       {
+               return FALSE;
+       }
+
+       return gtk_source_gutter_lines_has_qclass (lines, line, qname);
+}
+
+/**
+ * gtk_source_gutter_lines_has_qclass:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ * @qname: a #GQuark containing the class name
+ *
+ * Checks to see if gtk_source_gutter_lines_add_qclass() was called with
+ * the quark denoted by @qname for @line.
+ *
+ * Returns: %TRUE if @line contains @qname
+ *
+ * Since: 5.0
+ */
+gboolean
+gtk_source_gutter_lines_has_qclass (GtkSourceGutterLines *lines,
+                                    guint                 line,
+                                    GQuark                qname)
+{
+       LineInfo *info;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines), FALSE);
+       g_return_val_if_fail (qname != 0, FALSE);
+       g_return_val_if_fail (line >= lines->first, FALSE);
+       g_return_val_if_fail (line <= lines->last, FALSE);
+       g_return_val_if_fail (line - lines->first < lines->lines->len, FALSE);
+
+       info = &g_array_index (lines->lines, LineInfo, line - lines->first);
+
+       return quark_set_contains (&info->classes, qname);
+}
+
+/**
+ * gtk_source_gutter_lines_is_cursor:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ *
+ * Checks to see if @line contains the insertion cursor.
+ *
+ * Returns: %TRUE if the insertion cursor is on @line
+ *
+ * Since: 5.0
+ */
+gboolean
+gtk_source_gutter_lines_is_cursor (GtkSourceGutterLines *lines,
+                                   guint                 line)
+{
+       return gtk_source_gutter_lines_has_qclass (lines, line, q_cursor_line);
+}
+
+/**
+ * gtk_source_gutter_lines_is_prelit:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ *
+ * Checks to see if @line is marked as prelit. Generally, this means
+ * the mouse pointer is over the line within the gutter.
+ *
+ * Returns: %TRUE if the line is prelit
+ *
+ * Since: 5.0
+ */
+gboolean
+gtk_source_gutter_lines_is_prelit (GtkSourceGutterLines *lines,
+                                   guint                 line)
+{
+       return gtk_source_gutter_lines_has_qclass (lines, line, q_prelit);
+}
+
+/**
+ * gtk_source_gutter_lines_is_selected:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ *
+ * Checks to see if the view had a selection and if that selection overlaps
+ * @line in some way.
+ *
+ * Returns: %TRUE if the line contains a selection
+ *
+ * Since: 5.0
+ */
+gboolean
+gtk_source_gutter_lines_is_selected (GtkSourceGutterLines *lines,
+                                     guint                 line)
+{
+       return gtk_source_gutter_lines_has_qclass (lines, line, q_selected);
+}
+
+/**
+ * gtk_source_gutter_lines_get_first:
+ * @lines: a #GtkSourceGutterLines
+ *
+ * Gets the line number (starting from 0) for the first line that is
+ * user visible.
+ *
+ * Returns: a line number starting from 0
+ *
+ * Since: 5.0
+ */
+guint
+gtk_source_gutter_lines_get_first (GtkSourceGutterLines *lines)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines), 0);
+
+       return lines->first;
+}
+
+/**
+ * gtk_source_gutter_lines_get_last:
+ * @lines: a #GtkSourceGutterLines
+ *
+ * Gets the line number (starting from 0) for the last line that is
+ * user visible.
+ *
+ * Returns: a line number starting from 0
+ *
+ * Since: 5.0
+ */
+guint
+gtk_source_gutter_lines_get_last (GtkSourceGutterLines *lines)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines), 0);
+
+       return lines->last;
+}
+
+/**
+ * gtk_source_gutter_lines_get_iter_at_line:
+ * @lines: a #GtkSourceGutterLines
+ * @iter: (out): a location for a #GtkTextIter
+ * @line: the line number
+ *
+ * Gets a #GtkTextIter for the current buffer at @line
+ *
+ * Since: 5.0
+ */
+void
+gtk_source_gutter_lines_get_iter_at_line (GtkSourceGutterLines *lines,
+                                          GtkTextIter          *iter,
+                                          guint                 line)
+{
+       GtkTextBuffer *buffer;
+
+       g_return_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines));
+       g_return_if_fail (iter != NULL);
+
+       buffer = gtk_text_view_get_buffer (lines->view);
+       gtk_text_buffer_get_iter_at_line (buffer, iter, line);
+}
+
+/**
+ * gtk_source_gutter_lines_get_view:
+ * @lines: a #GtkSourceGutterLines
+ *
+ * Gets the #GtkTextView that the #GtkSourceGutterLines represents.
+ *
+ * Returns: (transfer none) (not nullable): a #GtkTextView
+ *
+ * Since: 5.0
+ */
+GtkTextView *
+gtk_source_gutter_lines_get_view (GtkSourceGutterLines *lines)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines), NULL);
+
+       return lines->view;
+}
+
+/**
+ * gtk_source_gutter_lines_get_buffer:
+ * @lines: a #GtkSourceGutterLines
+ *
+ * Gets the #GtkTextBuffer that the #GtkSourceGutterLines represents.
+ *
+ * Returns: (transfer none) (not nullable): a #GtkTextBuffer
+ *
+ * Since: 5.0
+ */
+GtkTextBuffer *
+gtk_source_gutter_lines_get_buffer (GtkSourceGutterLines *lines)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines), NULL);
+
+       return gtk_text_view_get_buffer (lines->view);
+}
+
+/**
+ * gtk_source_gutter_lines_get_line_yrange:
+ * @lines: a #GtkSourceGutterLines
+ * @line: a line number starting from zero
+ * @mode: a #GtkSourceGutterRendererAlignmentMode
+ * @y: (out): a location for the Y position in widget coordinates
+ * @height: (out): the line height based on @mode
+ *
+ * Gets the Y range for a line based on @mode.
+ *
+ * The value for @y is relative to the renderers widget coordinates.
+ *
+ * Since: 5.0
+ */
+void
+gtk_source_gutter_lines_get_line_yrange (GtkSourceGutterLines                 *lines,
+                                         guint                                 line,
+                                         GtkSourceGutterRendererAlignmentMode  mode,
+                                         gint                                 *y,
+                                         gint                                 *height)
+{
+       LineInfo *info;
+
+       g_return_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines));
+       g_return_if_fail (line >= lines->first);
+       g_return_if_fail (line <= lines->last);
+
+       info = &g_array_index (lines->lines, LineInfo, line - lines->first);
+
+       if (mode == GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_CELL)
+       {
+               *y = info->y;
+               *height = info->height;
+       }
+       else if (mode == GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_FIRST)
+       {
+               *y = info->y;
+               *height = info->first_height;
+       }
+       else if (mode == GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_LAST)
+       {
+               *y = info->y + info->height - info->last_height;
+               *height = info->last_height;
+       }
+       else
+       {
+               g_return_if_reached ();
+       }
+
+       *y -= lines->visible_rect.y;
+}
+
+guint
+_gtk_source_gutter_lines_get_cursor_line (GtkSourceGutterLines *lines)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_GUTTER_LINES (lines), 0);
+
+       return lines->cursor_line;
+}
diff --git a/gtksourceview/gtksourcegutterlines.h b/gtksourceview/gtksourcegutterlines.h
new file mode 100644
index 00000000..173f27c1
--- /dev/null
+++ b/gtksourceview/gtksourcegutterlines.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- /
+ *
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2019 - Christian Hergert <chergert redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
+#error "Only <gtksourceview/gtksource.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+
+#include "gtksourcetypes.h"
+#include "gtksourcegutterrenderer.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_GUTTER_LINES (gtk_source_gutter_lines_get_type())
+
+GTK_SOURCE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkSourceGutterLines, gtk_source_gutter_lines, GTK_SOURCE, GUTTER_LINES, GObject)
+
+GTK_SOURCE_AVAILABLE_IN_ALL
+guint          gtk_source_gutter_lines_get_first        (GtkSourceGutterLines                 *lines);
+GTK_SOURCE_AVAILABLE_IN_ALL
+guint          gtk_source_gutter_lines_get_last         (GtkSourceGutterLines                 *lines);
+GTK_SOURCE_AVAILABLE_IN_ALL
+void           gtk_source_gutter_lines_get_iter_at_line (GtkSourceGutterLines                 *lines,
+                                                         GtkTextIter                          *iter,
+                                                         guint                                 line);
+GTK_SOURCE_AVAILABLE_IN_ALL
+GtkTextView   *gtk_source_gutter_lines_get_view         (GtkSourceGutterLines                 *lines);
+GTK_SOURCE_AVAILABLE_IN_ALL
+GtkTextBuffer *gtk_source_gutter_lines_get_buffer       (GtkSourceGutterLines                 *lines);
+GTK_SOURCE_AVAILABLE_IN_ALL
+void           gtk_source_gutter_lines_get_yrange       (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line,
+                                                         guint                                *line_y,
+                                                         guint                                *line_height);
+GTK_SOURCE_AVAILABLE_IN_ALL
+void           gtk_source_gutter_lines_add_qclass       (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line,
+                                                         GQuark                                qname);
+GTK_SOURCE_AVAILABLE_IN_ALL
+void           gtk_source_gutter_lines_add_class        (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line,
+                                                         const gchar                          *name);
+GTK_SOURCE_AVAILABLE_IN_ALL
+void           gtk_source_gutter_lines_remove_class     (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line,
+                                                         const gchar                          *name);
+GTK_SOURCE_AVAILABLE_IN_ALL
+void           gtk_source_gutter_lines_remove_qclass    (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line,
+                                                         GQuark                                qname);
+GTK_SOURCE_AVAILABLE_IN_ALL
+gboolean       gtk_source_gutter_lines_has_class        (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line,
+                                                         const gchar                          *name);
+GTK_SOURCE_AVAILABLE_IN_ALL
+gboolean       gtk_source_gutter_lines_has_qclass       (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line,
+                                                         GQuark                                qname);
+GTK_SOURCE_AVAILABLE_IN_ALL
+gboolean       gtk_source_gutter_lines_is_cursor        (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line);
+GTK_SOURCE_AVAILABLE_IN_ALL
+gboolean       gtk_source_gutter_lines_is_prelit        (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line);
+GTK_SOURCE_AVAILABLE_IN_ALL
+gboolean       gtk_source_gutter_lines_is_selected      (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line);
+GTK_SOURCE_AVAILABLE_IN_ALL
+void           gtk_source_gutter_lines_get_line_yrange  (GtkSourceGutterLines                 *lines,
+                                                         guint                                 line,
+                                                         GtkSourceGutterRendererAlignmentMode  mode,
+                                                         gint                                 *y,
+                                                         gint                                 *height);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcetypes.h b/gtksourceview/gtksourcetypes.h
index d6632d59..3ac09423 100644
--- a/gtksourceview/gtksourcetypes.h
+++ b/gtksourceview/gtksourcetypes.h
@@ -47,6 +47,7 @@ typedef struct _GtkSourceFile                      GtkSourceFile;
 typedef struct _GtkSourceFileLoader                GtkSourceFileLoader;
 typedef struct _GtkSourceFileSaver                 GtkSourceFileSaver;
 typedef struct _GtkSourceGutter                    GtkSourceGutter;
+typedef struct _GtkSourceGutterLines               GtkSourceGutterLines;
 typedef struct _GtkSourceGutterRenderer            GtkSourceGutterRenderer;
 typedef struct _GtkSourceGutterRendererPixbuf      GtkSourceGutterRendererPixbuf;
 typedef struct _GtkSourceGutterRendererText        GtkSourceGutterRendererText;
diff --git a/gtksourceview/meson.build b/gtksourceview/meson.build
index 3a27dde6..2b5e7567 100644
--- a/gtksourceview/meson.build
+++ b/gtksourceview/meson.build
@@ -25,6 +25,7 @@ core_public_h = files([
   'gtksourceinit.h',
   'gtksourcelanguage.h',
   'gtksourcelanguagemanager.h',
+  'gtksourcegutterlines.h',
   'gtksourcemap.h',
   'gtksourcemark.h',
   'gtksourcemarkattributes.h',
@@ -64,6 +65,7 @@ core_public_c = files([
   'gtksourceinit.c',
   'gtksourcelanguage.c',
   'gtksourcelanguagemanager.c',
+  'gtksourcegutterlines.c',
   'gtksourcemap.c',
   'gtksourcemark.c',
   'gtksourcemarkattributes.c',
diff --git a/gtksourceview/quarkset-inline.h b/gtksourceview/quarkset-inline.h
new file mode 100644
index 00000000..c744c0a2
--- /dev/null
+++ b/gtksourceview/quarkset-inline.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _QuarkSet
+{
+       gint32 len;
+       union {
+               GQuark  embed[2];
+               GQuark *alloc;
+       } u;
+} QuarkSet;
+
+static inline gboolean
+quark_set_is_embed (QuarkSet *set)
+{
+       return set->len >= 0;
+}
+
+static inline void
+quark_set_clear (QuarkSet *set)
+{
+       if (set->len < 0)
+       {
+               g_free (set->u.alloc);
+       }
+
+       set->len = 0;
+       set->u.alloc = NULL;
+}
+
+static inline gboolean
+quark_set_contains (QuarkSet *set,
+                    GQuark    quark)
+{
+       GQuark *quarks;
+       guint i;
+       guint len;
+
+       if (set->len == 0)
+       {
+               return FALSE;
+       }
+
+       if (quark_set_is_embed (set))
+       {
+               quarks = set->u.embed;
+               len = set->len;
+       }
+       else
+       {
+               quarks = set->u.alloc;
+               len = ABS (set->len);
+       }
+
+       for (i = 0; i < len; i++)
+       {
+               if (quarks[i] == quark)
+               {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static inline void
+quark_set_add (QuarkSet *set,
+               GQuark    quark)
+{
+       if (quark_set_contains (set, quark))
+       {
+               return;
+       }
+
+       if G_LIKELY (set->len == 0 || set->len == 1)
+       {
+               G_STATIC_ASSERT (G_N_ELEMENTS (set->u.embed) == 2);
+
+               set->u.embed[set->len++] = quark;
+       }
+       else if (set->len == G_N_ELEMENTS (set->u.embed))
+       {
+               GQuark *alloc = g_new (GQuark, set->len + 1);
+               guint i;
+
+               for (i = 0; i < set->len; i++)
+               {
+                       alloc[i] = set->u.embed[i];
+               }
+
+               alloc[set->len] = quark;
+               set->len = -(set->len + 1);
+               set->u.alloc = alloc;
+       }
+       else if (set->len < 0)
+       {
+               guint len = ABS (set->len);
+
+               set->u.alloc = g_realloc_n (set->u.alloc, len + 1, sizeof (GQuark));
+               set->u.alloc[len] = quark;
+               set->len--; /* = -(len + 1) */
+       }
+       else
+       {
+               g_assert_not_reached ();
+       }
+}
+
+static inline void
+quark_set_remove (QuarkSet *set,
+                  GQuark    quark)
+{
+       if (set->len == 0)
+       {
+               return;
+       }
+       else if (set->len == -1 && set->u.alloc[0] == quark)
+       {
+               quark_set_clear (set);
+               return;
+       }
+       else if (set->len > 0)
+       {
+               G_STATIC_ASSERT (G_N_ELEMENTS (set->u.embed) == 2);
+
+               if (set->u.embed[0] == quark)
+               {
+                       set->u.embed[0] = set->u.embed[1];
+                       set->len--;
+               }
+               else if (set->u.embed[1] == quark)
+               {
+                       set->len--;
+               }
+       }
+       else if (set->len < 0)
+       {
+               guint len = ABS (set->len);
+               guint i;
+
+               for (i = 0; i < len; i++)
+               {
+                       if (set->u.alloc[i] == quark)
+                       {
+                               if (i + 1 < len)
+                               {
+                                       set->u.alloc[i] = set->u.alloc[len - 1];
+                               }
+
+                               set->len++; /* = -(len - 1) */
+
+                               break;
+                       }
+               }
+       }
+       else
+       {
+               g_assert_not_reached ();
+       }
+}
+
+G_END_DECLS


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