[gtksourceview] Implemented GtkSourceGutter
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: svn-commits-list gnome org
- Subject: [gtksourceview] Implemented GtkSourceGutter
- Date: Sat, 30 May 2009 06:36:10 -0400 (EDT)
commit 0d803836e458f3b816b22bb8f79b289a4139da70
Author: Jesse van den Kieboom <jesse icecrew nl>
Date: Sat May 30 12:34:06 2009 +0200
Implemented GtkSourceGutter
GtkSourceGutter replaces the custom drawing of lines and marks
and at the same time, provides a public API to allow users to extend
the gutter. The gutter works very much the same as a GtkCellLayout, in
the sense that you can pack GtkCellRenderer objects in the gutter.
---
docs/reference/Makefile.am | 3 +-
docs/reference/gtksourceview-2.0-sections.txt | 25 +
docs/reference/gtksourceview-2.0.types | 2 +
docs/reference/gtksourceview-docs.sgml | 1 +
docs/reference/tmpl/gutter.sgml | 149 +++
docs/reference/tmpl/view.sgml | 21 +-
gtksourceview/Makefile.am | 5 +-
gtksourceview/gtksourcegutter-private.h | 17 +
gtksourceview/gtksourcegutter.c | 1194 ++++++++++++++++++++
gtksourceview/gtksourcegutter.h | 108 ++
gtksourceview/gtksourceview-marshal.list | 3 +
gtksourceview/gtksourceview.c | 1434 ++++++++++++-------------
gtksourceview/gtksourceview.h | 13 +-
tests/test-widget.c | 87 +-
14 files changed, 2253 insertions(+), 809 deletions(-)
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 4506c5a..9675b51 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -33,7 +33,8 @@ IGNORE_HFILES = \
gtktextregion.h \
gtksourceengine.h \
gtksourcecontextengine.h \
- gtksourceprintjob.h
+ gtksourceprintjob.h \
+ gtksourcegutter-private.h
# Extra options to supply to gtkdoc-mkdb
MKDB_OPTIONS = --sgml-mode --output-format=xml --ignore-files=trio
diff --git a/docs/reference/gtksourceview-2.0-sections.txt b/docs/reference/gtksourceview-2.0-sections.txt
index 341efc1..c47424b 100644
--- a/docs/reference/gtksourceview-2.0-sections.txt
+++ b/docs/reference/gtksourceview-2.0-sections.txt
@@ -83,6 +83,7 @@ gtk_source_view_set_tab_width
gtk_source_view_get_tab_width
gtk_source_view_set_draw_spaces
gtk_source_view_get_draw_spaces
+gtk_source_view_get_gutter
<SUBSECTION Standard>
GTK_IS_SOURCE_VIEW
GTK_IS_SOURCE_VIEW_CLASS
@@ -95,6 +96,30 @@ gtk_source_view_get_type
</SECTION>
<SECTION>
+<FILE>gutter</FILE>
+<TITLE>GtkSourceGutter</TITLE>
+<INCLUDE>gtksourceview/gtksourcegutter.h</INCLUDE>
+GtkSourceGutter
+gtk_source_gutter_get_window
+gtk_source_gutter_insert
+gtk_source_gutter_reorder
+gtk_source_gutter_remove
+gtk_source_gutter_set_cell_data_func
+gtk_source_gutter_set_cell_size_func
+gtk_source_gutter_queue_draw
+<SUBSECTION Standard>
+GtkSourceGutterClass
+GTK_IS_SOURCE_GUTTER
+GTK_IS_SOURCE_GUTTER_CLASS
+GTK_SOURCE_GUTTER
+GTK_SOURCE_GUTTER_CLASS
+GTK_SOURCE_GUTTER_GET_CLASS
+GTK_TYPE_SOURCE_GUTTER
+GtkSourceGutterPrivate
+gtk_source_gutter_get_type
+</SECTION>
+
+<SECTION>
<FILE>language</FILE>
<TITLE>GtkSourceLanguage</TITLE>
<INCLUDE>gtksourceview/gtksourcelanguage.h</INCLUDE>
diff --git a/docs/reference/gtksourceview-2.0.types b/docs/reference/gtksourceview-2.0.types
index 74b795d..fe4d27d 100644
--- a/docs/reference/gtksourceview-2.0.types
+++ b/docs/reference/gtksourceview-2.0.types
@@ -5,6 +5,7 @@
#include <gtksourceview/gtksourcelanguagemanager.h>
#include <gtksourceview/gtksourcestyleschememanager.h>
#include <gtksourceview/gtksourcemark.h>
+#include <gtksourceview/gtksourcegutter.h>
gtk_source_view_get_type
gtk_source_buffer_get_type
@@ -15,3 +16,4 @@ gtk_source_style_get_type
gtk_source_style_scheme_get_type
gtk_source_style_scheme_manager_get_type
gtk_source_mark_get_type
+gtk_source_gutter_get_type
diff --git a/docs/reference/gtksourceview-docs.sgml b/docs/reference/gtksourceview-docs.sgml
index 0f74543..47cef36 100644
--- a/docs/reference/gtksourceview-docs.sgml
+++ b/docs/reference/gtksourceview-docs.sgml
@@ -15,6 +15,7 @@
<title>API reference</title>
<xi:include href="xml/buffer.xml"/>
<xi:include href="xml/iter.xml"/>
+ <xi:include href="xml/gutter.xml"/>
<xi:include href="xml/mark.xml"/>
<xi:include href="xml/view.xml"/>
<xi:include href="xml/language.xml"/>
diff --git a/docs/reference/tmpl/gutter.sgml b/docs/reference/tmpl/gutter.sgml
new file mode 100644
index 0000000..9ffb548
--- /dev/null
+++ b/docs/reference/tmpl/gutter.sgml
@@ -0,0 +1,149 @@
+<!-- ##### SECTION Title ##### -->
+GtkSourceGutter
+
+<!-- ##### SECTION Short_Description ##### -->
+Gutter object for #GtkSourceView
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+The #GtkSourceGutter object represents the left and right gutters of the
+text view. It is used by #GtkSourceView to draw the line numbers and category
+marks that might be present on a line. By packing additional #GtkCellRenderer
+objects in the gutter, you can extend the gutter by your own custom drawings.
+</para>
+<para>
+The gutter works very much the same way as cells rendered in a #GtkTreeView.
+The concept is similar, with the exception that the gutter does not have an
+underlying #GtkTreeModel. Instead, you should use
+#gtk_source_gutter_set_cell_data_func to set a callback to fill in any
+of the cell renderers properties, given the line for which the cell is to be
+rendered. Renderers are inserted into the gutter at a certain position.
+The builtin line number renderer is at position
+#GTK_SOURCE_VIEW_GUTTER_POSITION_LINES (-30) and the marks renderer is at
+#GTK_SOURCE_VIEW_GUTTER_POSITION_MARKS (-20). You can use these values to
+position custom renderers accordingly.
+</para>
+<para>
+The width of a cell renderer can be specified as either fixed (using
+#gtk_cell_renderer_set_fixed_size) or dynamic, in which case you
+<emphasis>MUST</emphasis> set
+#gtk_source_gutter_set_cell_size_func. This callback is used to set the
+properties of the renderer such that #gtk_cell_renderer_get_size yields the
+maximum width of the cell.
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GtkSourceGutter ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### SIGNAL GtkSourceGutter::cell-activated ##### -->
+<para>
+
+</para>
+
+ sourcegutter: the object which received the signal.
+ arg1:
+ arg2:
+ arg3:
+
+<!-- ##### SIGNAL GtkSourceGutter::query-tooltip ##### -->
+<para>
+
+</para>
+
+ sourcegutter: the object which received the signal.
+ arg1:
+ arg2:
+ arg3:
+ Returns:
+
+<!-- ##### ARG GtkSourceGutter:view ##### -->
+<para>
+
+</para>
+
+<!-- ##### ARG GtkSourceGutter:window-type ##### -->
+<para>
+
+</para>
+
+<!-- ##### FUNCTION gtk_source_gutter_get_window ##### -->
+<para>
+
+</para>
+
+ gutter:
+ Returns:
+
+
+<!-- ##### FUNCTION gtk_source_gutter_insert ##### -->
+<para>
+
+</para>
+
+ gutter:
+ renderer:
+ position:
+
+
+<!-- ##### FUNCTION gtk_source_gutter_reorder ##### -->
+<para>
+
+</para>
+
+ gutter:
+ renderer:
+ position:
+
+
+<!-- ##### FUNCTION gtk_source_gutter_remove ##### -->
+<para>
+
+</para>
+
+ gutter:
+ renderer:
+
+
+<!-- ##### FUNCTION gtk_source_gutter_set_cell_data_func ##### -->
+<para>
+
+</para>
+
+ gutter:
+ renderer:
+ func:
+ func_data:
+ destroy:
+
+
+<!-- ##### FUNCTION gtk_source_gutter_set_cell_size_func ##### -->
+<para>
+
+</para>
+
+ gutter:
+ renderer:
+ func:
+ func_data:
+ destroy:
+
+
+<!-- ##### FUNCTION gtk_source_gutter_queue_draw ##### -->
+<para>
+
+</para>
+
+ gutter:
+
+
diff --git a/docs/reference/tmpl/view.sgml b/docs/reference/tmpl/view.sgml
index a527c75..39511cf 100644
--- a/docs/reference/tmpl/view.sgml
+++ b/docs/reference/tmpl/view.sgml
@@ -25,6 +25,15 @@ a text view which syntax highlighting, undo/redo and text marks. Use a
</para>
+<!-- ##### SIGNAL GtkSourceView::line-mark-activated ##### -->
+<para>
+
+</para>
+
+ sourceview: the object which received the signal.
+ arg1:
+ arg2:
+
<!-- ##### SIGNAL GtkSourceView::redo ##### -->
<para>
@@ -107,10 +116,10 @@ a text view which syntax highlighting, undo/redo and text marks. Use a
@parent_class:
@undo:
@redo:
+ line_mark_activated:
@_gtk_source_reserved1:
@_gtk_source_reserved2:
@_gtk_source_reserved3:
- _gtk_source_reserved4:
<!-- ##### USER_FUNCTION GtkSourceViewMarkTooltipFunc ##### -->
<para>
@@ -461,3 +470,13 @@ Function type for setting up a tooltip for #GtkSourceMark.
@Returns:
+<!-- ##### FUNCTION gtk_source_view_get_gutter ##### -->
+<para>
+
+</para>
+
+ view:
+ window_type:
+ Returns:
+
+
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index e646d1d..75063ab 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -27,7 +27,8 @@ libgtksourceview_headers = \
gtksourcestyleschememanager.h \
gtksourcestylescheme.h \
gtksourcemark.h \
- gtksourceprintcompositor.h
+ gtksourceprintcompositor.h \
+ gtksourcegutter.h
libgtksourceview_2_0_la_SOURCES = \
gtksourcebuffer.c \
@@ -56,6 +57,8 @@ libgtksourceview_2_0_la_SOURCES = \
gtksourcecontextengine.c \
gtksourcemark.c \
gtksourceprintcompositor.c \
+ gtksourcegutter-private.h \
+ gtksourcegutter.c \
$(libgtksourceview_headers)
# do not distribute generated files
diff --git a/gtksourceview/gtksourcegutter-private.h b/gtksourceview/gtksourcegutter-private.h
new file mode 100644
index 0000000..bd96a78
--- /dev/null
+++ b/gtksourceview/gtksourcegutter-private.h
@@ -0,0 +1,17 @@
+#ifndef __GTK_SOURCE_GUTTER_PRIVATE_H__
+#define __GTK_SOURCE_GUTTER_PRIVATE_H__
+
+#include "gtksourcegutter.h"
+
+G_BEGIN_DECLS
+
+struct _GtkSourceView;
+
+GtkSourceGutter *gtk_source_gutter_new (struct _GtkSourceView *view,
+ GtkTextWindowType type);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_GUTTER_PRIVATE_H__ */
+
+/* vi:ts=8 */
diff --git a/gtksourceview/gtksourcegutter.c b/gtksourceview/gtksourcegutter.c
new file mode 100644
index 0000000..8599828
--- /dev/null
+++ b/gtksourceview/gtksourcegutter.c
@@ -0,0 +1,1194 @@
+/*
+ * gtksourcegutter.c
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include "gtksourcegutter-private.h"
+#include "gtksourceview.h"
+#include "gtksourceview-i18n.h"
+#include "gtksourceview-marshal.h"
+
+#define GTK_SOURCE_GUTTER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GTK_TYPE_SOURCE_GUTTER, GtkSourceGutterPrivate))
+
+/* Properties */
+enum
+{
+ PROP_0,
+ PROP_VIEW,
+ PROP_WINDOW_TYPE
+};
+
+/* Signals */
+enum
+{
+ CELL_ACTIVATED,
+ QUERY_TOOLTIP,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0,};
+
+typedef struct
+{
+ GtkCellRenderer *renderer;
+ gint position;
+
+ GtkSourceGutterDataFunc data_func;
+ gpointer data_func_data;
+ GDestroyNotify data_func_destroy;
+
+ GtkSourceGutterSizeFunc size_func;
+ gpointer size_func_data;
+ GDestroyNotify size_func_destroy;
+} Renderer;
+
+enum
+{
+ EXPOSE_EVENT,
+ MOTION_NOTIFY_EVENT,
+ BUTTON_PRESS_EVENT,
+ ENTER_NOTIFY_EVENT,
+ LEAVE_NOTIFY_EVENT,
+ QUERY_TOOLTIP_EVENT,
+ LAST_EXTERNAL_SIGNAL
+};
+
+struct _GtkSourceGutterPrivate
+{
+ GtkSourceView *view;
+ GtkTextWindowType window_type;
+
+ GList *renderers;
+ guint signals[LAST_EXTERNAL_SIGNAL];
+};
+
+G_DEFINE_TYPE (GtkSourceGutter, gtk_source_gutter, G_TYPE_OBJECT)
+
+static gboolean on_view_expose_event (GtkSourceView *view,
+ GdkEventExpose *event,
+ GtkSourceGutter *gutter);
+
+static gboolean on_view_motion_notify_event (GtkSourceView *view,
+ GdkEventMotion *event,
+ GtkSourceGutter *gutter);
+
+
+static gboolean on_view_enter_notify_event (GtkSourceView *view,
+ GdkEventCrossing *event,
+ GtkSourceGutter *gutter);
+
+static gboolean on_view_leave_notify_event (GtkSourceView *view,
+ GdkEventCrossing *event,
+ GtkSourceGutter *gutter);
+
+static gboolean on_view_button_press_event (GtkSourceView *view,
+ GdkEventButton *event,
+ GtkSourceGutter *gutter);
+
+static gboolean on_view_query_tooltip (GtkSourceView *view,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ GtkSourceGutter *gutter);
+
+static void
+view_notify (GtkSourceGutter *gutter,
+ gpointer where_the_object_was)
+{
+ gutter->priv->view = NULL;
+}
+
+static Renderer *
+renderer_new (GtkCellRenderer *renderer,
+ gint position)
+{
+ Renderer *ret = g_slice_new0 (Renderer);
+
+ ret->renderer = g_object_ref (renderer);
+ ret->position = position;
+
+ return ret;
+}
+
+static void
+renderer_free (Renderer *renderer)
+{
+ if (renderer->data_func_destroy && renderer->data_func_data)
+ {
+ renderer->data_func_destroy (renderer->data_func_data);
+ }
+
+ if (renderer->size_func_destroy && renderer->size_func_data)
+ {
+ renderer->size_func_destroy (renderer->size_func_data);
+ }
+
+ g_object_unref (renderer->renderer);
+ g_slice_free (Renderer, renderer);
+}
+
+static void
+gtk_source_gutter_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gtk_source_gutter_parent_class)->finalize (object);
+}
+
+static void
+gtk_source_gutter_dispose (GObject *object)
+{
+ GtkSourceGutter *gutter = GTK_SOURCE_GUTTER (object);
+ gint i;
+
+ g_list_foreach (gutter->priv->renderers, (GFunc)renderer_free, NULL);
+ g_list_free (gutter->priv->renderers);
+
+ if (gutter->priv->view)
+ {
+ for (i = 0; i < LAST_EXTERNAL_SIGNAL; ++i)
+ {
+ g_signal_handler_disconnect (gutter->priv->view,
+ gutter->priv->signals[i]);
+ }
+
+ g_object_weak_unref (G_OBJECT (gutter->priv->view),
+ (GWeakNotify)view_notify,
+ gutter);
+
+ gutter->priv->view = NULL;
+ }
+
+ gutter->priv->renderers = NULL;
+}
+
+static void
+gtk_source_gutter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSourceGutter *self = GTK_SOURCE_GUTTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_VIEW:
+ g_value_set_object (value, self->priv->view);
+ break;
+ case PROP_WINDOW_TYPE:
+ g_value_set_enum (value, self->priv->window_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_view (GtkSourceGutter *gutter,
+ GtkSourceView *view)
+{
+ gutter->priv->view = view;
+
+ g_object_weak_ref (G_OBJECT (view),
+ (GWeakNotify)view_notify,
+ gutter);
+
+ gutter->priv->signals[EXPOSE_EVENT] =
+ g_signal_connect (view,
+ "expose-event",
+ G_CALLBACK (on_view_expose_event),
+ gutter);
+
+ gutter->priv->signals[MOTION_NOTIFY_EVENT] =
+ g_signal_connect (view,
+ "motion-notify-event",
+ G_CALLBACK (on_view_motion_notify_event),
+ gutter);
+
+ gutter->priv->signals[ENTER_NOTIFY_EVENT] =
+ g_signal_connect (view,
+ "enter-notify-event",
+ G_CALLBACK (on_view_enter_notify_event),
+ gutter);
+
+ gutter->priv->signals[LEAVE_NOTIFY_EVENT] =
+ g_signal_connect (view,
+ "leave-notify-event",
+ G_CALLBACK (on_view_leave_notify_event),
+ gutter);
+
+ gutter->priv->signals[BUTTON_PRESS_EVENT] =
+ g_signal_connect (view,
+ "button-press-event",
+ G_CALLBACK (on_view_button_press_event),
+ gutter);
+
+ gutter->priv->signals[QUERY_TOOLTIP_EVENT] =
+ g_signal_connect (view,
+ "query-tooltip",
+ G_CALLBACK (on_view_query_tooltip),
+ gutter);
+}
+
+static void
+do_redraw (GtkSourceGutter *gutter)
+{
+ GdkWindow *window;
+
+ window = gtk_text_view_get_window (GTK_TEXT_VIEW (gutter->priv->view),
+ gutter->priv->window_type);
+
+ if (window)
+ {
+ gdk_window_invalidate_rect (window, NULL, FALSE);
+ }
+}
+
+static void
+revalidate_size (GtkSourceGutter *gutter)
+{
+ GdkWindow *window;
+
+ window = gtk_source_gutter_get_window (gutter);
+
+ if (!window && gutter->priv->renderers)
+ {
+ /* Make window visible by setting its size to minimum size,
+ actual size will be calculated in expose */
+ gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (gutter->priv->view),
+ gutter->priv->window_type,
+ 1);
+ do_redraw (gutter);
+ }
+ else if (window && !gutter->priv->renderers)
+ {
+ /* Make window invisible by setting size to 0 */
+ gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (gutter->priv->view),
+ gutter->priv->window_type,
+ 0);
+ }
+ else if (window)
+ {
+ /* Redraw the window. Actual size will be calculated in expose */
+ do_redraw (gutter);
+ }
+}
+
+static void
+gtk_source_gutter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSourceGutter *self = GTK_SOURCE_GUTTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_VIEW:
+ set_view (self, GTK_SOURCE_VIEW (g_value_get_object (value)));
+
+ break;
+ case PROP_WINDOW_TYPE:
+ self->priv->window_type = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_source_gutter_class_init (GtkSourceGutterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gtk_source_gutter_set_property;
+ object_class->get_property = gtk_source_gutter_get_property;
+
+ object_class->finalize = gtk_source_gutter_finalize;
+ object_class->dispose = gtk_source_gutter_dispose;
+
+ /**
+ * GtkSourceGutter:view:
+ *
+ * The #GtkSourceView of the gutter
+ */
+ g_object_class_install_property (object_class,
+ PROP_VIEW,
+ g_param_spec_object ("view",
+ _("View"),
+ _("The gutters' GtkSourceView"),
+ GTK_TYPE_SOURCE_VIEW,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GtkSourceGutter:window-type:
+ *
+ * The text window type on which the window is placed
+ */
+ g_object_class_install_property (object_class,
+ PROP_WINDOW_TYPE,
+ g_param_spec_enum ("window_type",
+ _("Window Type"),
+ _("The gutters text window type"),
+ GTK_TYPE_TEXT_WINDOW_TYPE,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+
+ /**
+ * GtkSourceGutter::cell-activated:
+ * @gutter: the #GtkSourceGutter
+ * @renderer: the #GtkCellRenderer which was activated
+ * @iter: the #GtkTextIter at which the cell was activated
+ * @event: the #GdkEvent with which the cell was activated
+ *
+ * Emitted when a cell has been activated (for instance when there was
+ * a button press on the cell). The signal is only emitted for cells
+ * that have the #activatable property set to %TRUE.
+ */
+ signals [CELL_ACTIVATED] =
+ g_signal_new ("cell-activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkSourceGutterClass, cell_activated),
+ NULL,
+ NULL,
+ _gtksourceview_marshal_VOID__OBJECT_BOXED_POINTER,
+ G_TYPE_NONE,
+ 3,
+ GTK_TYPE_CELL_RENDERER,
+ GTK_TYPE_TEXT_ITER,
+ G_TYPE_POINTER);
+
+ /**
+ * GtkSourceGutter::query-tooltip:
+ * @gutter: the #GtkSourceGutter
+ * @renderer: the #GtkCellRenderer which was activated
+ * @iter: the #GtkTextIter at which the cell was activated
+ * @tooltip: the #GtkTooltip
+ *
+ * Emitted when a tooltip is requested for a specific cell. Signal
+ * handlers can return %TRUE to notify the tooltip has been handled.
+ */
+ signals [QUERY_TOOLTIP] =
+ g_signal_new ("query-tooltip",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkSourceGutterClass, query_tooltip),
+ g_signal_accumulator_true_handled,
+ NULL,
+ _gtksourceview_marshal_BOOL__OBJECT_BOXED_OBJECT,
+ G_TYPE_BOOLEAN,
+ 3,
+ GTK_TYPE_CELL_RENDERER,
+ GTK_TYPE_TEXT_ITER,
+ GTK_TYPE_TOOLTIP);
+
+ g_type_class_add_private (object_class, sizeof(GtkSourceGutterPrivate));
+}
+
+static void
+gtk_source_gutter_init (GtkSourceGutter *self)
+{
+ self->priv = GTK_SOURCE_GUTTER_GET_PRIVATE (self);
+}
+
+static gint
+sort_by_position (Renderer *r1,
+ Renderer *r2,
+ gpointer data)
+{
+ if (r1->position < r2->position)
+ {
+ return -1;
+ }
+ else if (r1->position > r2->position)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static void
+append_renderer (GtkSourceGutter *gutter,
+ Renderer *renderer)
+{
+ GList *item;
+
+ gutter->priv->renderers =
+ g_list_insert_sorted_with_data (gutter->priv->renderers,
+ renderer,
+ (GCompareDataFunc)sort_by_position,
+ NULL);
+}
+
+GtkSourceGutter *
+gtk_source_gutter_new (GtkSourceView *view,
+ GtkTextWindowType type)
+{
+ return g_object_new (GTK_TYPE_SOURCE_GUTTER,
+ "view", view,
+ "window_type", type,
+ NULL);
+}
+
+/* Public API */
+
+/**
+ * gtk_source_gutter_get_window:
+ * @gutter: a #GtkSourceGutter
+ *
+ * Get the #GdkWindow of the gutter. The window will only be available when the
+ * gutter has at least one, non-zero width, cell renderer packed.
+ *
+ * Returns: the #GdkWindow of the gutter, or %NULL if the gutter has no window.
+ *
+ * Since: 2.8
+ */
+GdkWindow *
+gtk_source_gutter_get_window (GtkSourceGutter *gutter)
+{
+ g_return_val_if_fail (GTK_IS_SOURCE_GUTTER (gutter), NULL);
+ g_return_val_if_fail (gutter->priv->view != NULL, NULL);
+
+ return gtk_text_view_get_window (GTK_TEXT_VIEW (gutter->priv->view),
+ gutter->priv->window_type);
+}
+
+/**
+ * gtk_source_gutter_insert:
+ * @gutter: a #GtkSourceGutter
+ * @renderer: a #GtkCellRenderer
+ * @position: the renderers position
+ *
+ * Inserts @renderer into @gutter at @position.
+ *
+ * Since: 2.8
+ */
+void
+gtk_source_gutter_insert (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ gint position)
+{
+ g_return_if_fail (GTK_IS_SOURCE_GUTTER (gutter));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ append_renderer (gutter, renderer_new (renderer, position));
+ revalidate_size (gutter);
+}
+
+static gboolean
+renderer_find (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ Renderer **ret,
+ GList **retlist)
+{
+ GList *list;
+
+ for (list = gutter->priv->renderers; list; list = g_list_next (list))
+ {
+ *ret = (Renderer *)list->data;
+
+ if ((*ret)->renderer == renderer)
+ {
+ if (retlist)
+ {
+ *retlist = list;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * gtk_source_gutter_reorder:
+ * @gutter: a #GtkSourceGutter
+ * @renderer: a #GtkCellRenderer
+ * @position: the new renderer position
+ *
+ * Reorders @renderer in @gutter to new @position.
+ *
+ * Since: 2.8
+ */
+void
+gtk_source_gutter_reorder (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ gint position)
+{
+ Renderer *ret;
+ GList *retlist;
+
+ g_return_if_fail (GTK_IS_SOURCE_GUTTER (gutter));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ if (renderer_find (gutter, renderer, &ret, &retlist))
+ {
+ gutter->priv->renderers = g_list_remove_link (gutter->priv->renderers, retlist);
+ append_renderer (gutter, renderer);
+ }
+}
+
+/**
+ * gtk_source_gutter_remove:
+ * @gutter: a #GtkSourceGutter
+ * @renderer: a #GtkCellRenderer
+ *
+ * Removes @renderer from @gutter.
+ *
+ * Since: 2.8
+ */
+void
+gtk_source_gutter_remove (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer)
+{
+ Renderer *ret;
+ GList *retlist;
+
+ g_return_if_fail (GTK_IS_SOURCE_GUTTER (gutter));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ if (renderer_find (gutter, renderer, &ret, &retlist))
+ {
+ gutter->priv->renderers = g_list_remove_link (gutter->priv->renderers, retlist);
+
+ revalidate_size (gutter);
+ renderer_free (ret);
+ }
+}
+
+/**
+ * gtk_source_gutter_set_cell_data_func:
+ * @gutter: a #GtkSourceGutter
+ * @renderer: a #GtkCellRenderer
+ * @func: the #GtkSourceGutterDataFunc to use
+ * @func_data: the user data for @func
+ * @destroy: the destroy notification for @func_data
+ *
+ * Sets the #GtkSourceGutterDataFunc to use for @renderer. This function is
+ * used to setup the cell renderer properties for rendering the current cell.
+ *
+ * Since: 2.8
+ */
+void
+gtk_source_gutter_set_cell_data_func (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkSourceGutterDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy)
+{
+ Renderer *ret;
+
+ g_return_if_fail (GTK_IS_SOURCE_GUTTER (gutter));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ if (!renderer_find (gutter, renderer, &ret, NULL))
+ {
+ return;
+ }
+
+ if (ret->data_func_data && ret->data_func_destroy)
+ {
+ ret->data_func_destroy (ret->data_func_data);
+ }
+
+ ret->data_func = func;
+ ret->data_func_data = func_data;
+ ret->data_func_destroy = destroy;
+
+ revalidate_size (gutter);
+}
+
+/**
+ * gtk_source_gutter_set_cell_size_func:
+ * @gutter: a #GtkSourceGutter
+ * @renderer: a #GtkCellRenderer
+ * @func: the #GtkSourceGutterSizeFunc to use
+ * @func_data: the user data for @func
+ * @destroy: the destroy notification for @func_data
+ *
+ * Sets the #GtkSourceGutterSizeFunc to use for @renderer. This function is
+ * used to setup the cell renderer properties for measuring the maximum size
+ * of the cell.
+ *
+ * Since: 2.8
+ */
+void
+gtk_source_gutter_set_cell_size_func (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkSourceGutterSizeFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy)
+{
+ Renderer *ret;
+
+ g_return_if_fail (GTK_IS_SOURCE_GUTTER (gutter));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+
+ if (!renderer_find (gutter, renderer, &ret, NULL))
+ {
+ return;
+ }
+
+ if (ret->size_func_data && ret->size_func_destroy)
+ {
+ ret->size_func_destroy (ret->size_func_data);
+ }
+
+ ret->size_func = func;
+ ret->size_func_data = func_data;
+ ret->size_func_destroy = destroy;
+
+ revalidate_size (gutter);
+}
+
+/**
+ * gtk_source_gutter_queue_draw:
+ * @gutter: a #GtkSourceGutter
+ *
+ * Invalidates the drawable area of the gutter. You can use this to force a
+ * redraw of the gutter if something has changed and needs to be redrawn.
+ *
+ * Since: 2.8
+ */
+void
+gtk_source_gutter_queue_draw (GtkSourceGutter *gutter)
+{
+ g_return_if_fail (GTK_IS_SOURCE_GUTTER (gutter));
+
+ revalidate_size (gutter);
+}
+
+/* Callbacks */
+static gint
+calculate_size (GtkSourceGutter *gutter,
+ Renderer *renderer)
+{
+ gint width = -1;
+
+ gtk_cell_renderer_get_fixed_size (renderer->renderer, &width, NULL);
+
+ if (width == -1 && renderer->size_func)
+ {
+ gint height;
+ renderer->size_func (gutter, renderer->renderer, renderer->size_func_data);
+
+ gtk_cell_renderer_get_size (renderer->renderer,
+ GTK_WIDGET (gutter->priv->view),
+ NULL, NULL, NULL,
+ &width, &height);
+ }
+
+ return width == -1 ? 1 : width;
+}
+
+static gint
+calculate_sizes (GtkSourceGutter *gutter,
+ GArray *sizes)
+{
+ GList *item;
+ gint total_width = 0;
+
+ /* Calculate size */
+ for (item = gutter->priv->renderers; item; item = g_list_next (item))
+ {
+ Renderer *renderer = (Renderer *)item->data;
+ gint width;
+
+ width = calculate_size (gutter, renderer);
+ g_array_append_val (sizes, width);
+
+ total_width += width;
+ }
+
+ return total_width;
+}
+
+/* This function is taken from gtk+/tests/testtext.c */
+static void
+get_lines (GtkTextView *text_view,
+ gint first_y,
+ gint last_y,
+ GArray *buffer_coords,
+ GArray *line_heights,
+ GArray *numbers,
+ gint *countp)
+{
+ GtkTextIter iter;
+ gint count;
+ gint size;
+ gint last_line_num = -1;
+
+ g_array_set_size (buffer_coords, 0);
+ g_array_set_size (numbers, 0);
+
+ if (line_heights != NULL)
+ g_array_set_size (line_heights, 0);
+
+ /* Get iter at first y */
+ gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
+
+ /* For each iter, get its location and add it to the arrays.
+ * Stop when we pass last_y */
+ count = 0;
+ size = 0;
+
+ while (!gtk_text_iter_is_end (&iter))
+ {
+ gint y, height;
+
+ gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
+
+ g_array_append_val (buffer_coords, y);
+
+ if (line_heights)
+ {
+ g_array_append_val (line_heights, height);
+ }
+
+ last_line_num = gtk_text_iter_get_line (&iter);
+ g_array_append_val (numbers, last_line_num);
+
+ ++count;
+
+ if ((y + height) >= last_y)
+ break;
+
+ gtk_text_iter_forward_line (&iter);
+ }
+
+ if (gtk_text_iter_is_end (&iter))
+ {
+ gint y, height;
+ gint line_num;
+
+ gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
+
+ line_num = gtk_text_iter_get_line (&iter);
+
+ if (line_num != last_line_num)
+ {
+ g_array_append_val (buffer_coords, y);
+
+ if (line_heights)
+ {
+ g_array_append_val (line_heights, height);
+ }
+
+ g_array_append_val (numbers, line_num);
+ ++count;
+ }
+ }
+
+ *countp = count;
+
+ if (count == 0)
+ {
+ gint y = 0;
+ gint n = 0;
+ gint height;
+
+ *countp = 1;
+
+ g_array_append_val (buffer_coords, y);
+ g_array_append_val (numbers, n);
+
+ if (line_heights)
+ {
+ gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
+ g_array_append_val (line_heights, height);
+ }
+ }
+}
+
+static gboolean
+on_view_expose_event (GtkSourceView *view,
+ GdkEventExpose *event,
+ GtkSourceGutter *gutter)
+{
+ GdkWindow *window;
+ GtkTextView *text_view;
+ gint size;
+ gint y1, y2;
+ GArray *numbers;
+ GArray *pixels;
+ GArray *heights;
+ GArray *sizes;
+ GtkTextIter cur;
+ gint cur_line;
+ gint count;
+ gint i;
+ GList *item;
+ gint x;
+ gint y;
+
+ window = gtk_source_gutter_get_window (gutter);
+
+ if (window == NULL || event->window != window)
+ {
+ return FALSE;
+ }
+
+ gdk_window_get_pointer (window, &x, &y, NULL);
+
+ text_view = GTK_TEXT_VIEW (view);
+ sizes = g_array_new (FALSE, FALSE, sizeof (gint));
+
+ size = calculate_sizes (gutter, sizes);
+
+ if (gtk_text_view_get_border_window_size (text_view, gutter->priv->window_type) != size)
+ {
+ /* Will trigger a new expose */
+ gtk_text_view_set_border_window_size (text_view, gutter->priv->window_type, size);
+ return FALSE;
+ }
+
+ y1 = event->area.y;
+ y2 = y1 + event->area.height;
+
+ /* get the extents of the line printing */
+ gtk_text_view_window_to_buffer_coords (text_view,
+ gutter->priv->window_type,
+ 0,
+ y1,
+ NULL,
+ &y1);
+
+ gtk_text_view_window_to_buffer_coords (text_view,
+ gutter->priv->window_type,
+ 0,
+ y2,
+ NULL,
+ &y2);
+
+ numbers = g_array_new (FALSE, FALSE, sizeof (gint));
+ pixels = g_array_new (FALSE, FALSE, sizeof (gint));
+ heights = g_array_new (FALSE, FALSE, sizeof (gint));
+
+ /* get the line numbers and y coordinates. */
+ get_lines (text_view, y1, y2, pixels, heights, numbers, &count);
+
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer,
+ &cur,
+ gtk_text_buffer_get_insert (text_view->buffer));
+
+ cur_line = gtk_text_iter_get_line (&cur);
+
+ for (i = 0; i < count; ++i)
+ {
+ gint pos;
+ gint line_to_paint;
+ GdkRectangle cell_area;
+ gint idx = 0;
+
+ gtk_text_view_buffer_to_window_coords (text_view,
+ gutter->priv->window_type,
+ 0,
+ g_array_index (pixels, gint, i),
+ NULL,
+ &pos);
+
+ line_to_paint = g_array_index (numbers, gint, i);
+
+ cell_area.x = 0;
+ cell_area.y = pos;
+ cell_area.height = g_array_index (heights, gint, i);
+
+ for (item = gutter->priv->renderers; item; item = g_list_next (item))
+ {
+ Renderer *renderer = (Renderer *)item->data;
+ gint width = g_array_index (sizes, gint, idx++);
+ GtkCellRendererState state = 0;
+
+ cell_area.width = width;
+
+ /* Call data func if needed */
+ if (renderer->data_func)
+ {
+ renderer->data_func (gutter,
+ renderer->renderer,
+ line_to_paint,
+ line_to_paint == cur_line,
+ renderer->data_func_data);
+ }
+
+ if (x >= cell_area.x && x < cell_area.x + cell_area.width &&
+ y >= cell_area.y && y < cell_area.y + cell_area.height)
+ {
+ GtkCellRendererMode mode;
+
+ g_object_get (G_OBJECT (renderer->renderer),
+ "mode", &mode,
+ NULL);
+
+ if (mode & GTK_CELL_RENDERER_MODE_ACTIVATABLE)
+ state = GTK_CELL_RENDERER_PRELIT;
+ }
+
+ /* Call render with correct area */
+ gtk_cell_renderer_render (renderer->renderer,
+ window,
+ GTK_WIDGET (view),
+ &cell_area,
+ &cell_area,
+ &cell_area,
+ state);
+
+ cell_area.x += cell_area.width;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+redraw_for_window (GtkSourceGutter *gutter,
+ GdkEventAny *event,
+ gboolean act_on_window)
+{
+ if (event->window == gtk_source_gutter_get_window (gutter) || !act_on_window)
+ {
+ gtk_source_gutter_queue_draw (gutter);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+on_view_motion_notify_event (GtkSourceView *view,
+ GdkEventMotion *event,
+ GtkSourceGutter *gutter)
+{
+ return redraw_for_window (gutter, (GdkEventAny *)event, TRUE);
+}
+
+
+static gboolean
+on_view_enter_notify_event (GtkSourceView *view,
+ GdkEventCrossing *event,
+ GtkSourceGutter *gutter)
+{
+ return redraw_for_window (gutter, (GdkEventAny *)event, TRUE);
+}
+
+
+static gboolean
+on_view_leave_notify_event (GtkSourceView *view,
+ GdkEventCrossing *event,
+ GtkSourceGutter *gutter)
+{
+ return redraw_for_window (gutter, (GdkEventAny *)event, FALSE);
+}
+
+static Renderer *
+renderer_at_x (GtkSourceGutter *gutter,
+ gint x,
+ gint *start,
+ gint *width)
+{
+ GList *item;
+
+ for (item = gutter->priv->renderers; item; item = g_list_next (item))
+ {
+ Renderer *renderer = (Renderer *)item->data;
+
+ *width = calculate_size (gutter, renderer);
+
+ if (x >= *start && x < *start + *width)
+ {
+ return renderer;
+ }
+
+ *start += *width;
+ }
+
+ return NULL;
+}
+
+static gboolean
+on_view_button_press_event (GtkSourceView *view,
+ GdkEventButton *event,
+ GtkSourceGutter *gutter)
+{
+ Renderer *renderer;
+ gint yline;
+ GtkTextIter line_iter;
+ GtkTextIter cur;
+ gint cur_line;
+ gint line;
+ gint y_buf;
+ gint start = 0;
+ gint width = 0;
+ GtkTextBuffer *buffer;
+ GtkCellRendererMode mode;
+
+ if (event->window != gtk_source_gutter_get_window (gutter))
+ {
+ return FALSE;
+ }
+
+ /* Check cell renderer */
+ renderer = renderer_at_x (gutter, event->x, &start, &width);
+
+ if (!renderer)
+ {
+ return FALSE;
+ }
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
+ gutter->priv->window_type,
+ event->x, event->y,
+ NULL, &y_buf);
+
+ gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view),
+ &line_iter,
+ y_buf,
+ &yline);
+
+ if (yline > y_buf)
+ {
+ return FALSE;
+ }
+
+ line = gtk_text_iter_get_line (&line_iter);
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ gtk_text_buffer_get_iter_at_mark (buffer,
+ &cur,
+ gtk_text_buffer_get_insert (buffer));
+
+ cur_line = gtk_text_iter_get_line (&cur);
+
+ if (renderer->data_func)
+ {
+ renderer->data_func (gutter,
+ renderer->renderer,
+ line,
+ line == cur_line,
+ renderer->data_func_data);
+ }
+
+ g_object_get (G_OBJECT (renderer->renderer),
+ "mode", &mode,
+ NULL);
+
+ if (mode & GTK_CELL_RENDERER_MODE_ACTIVATABLE)
+ {
+ GdkRectangle area;
+ gchar *path;
+ gboolean ret;
+
+ gtk_text_view_get_line_yrange (GTK_TEXT_VIEW (view),
+ &line_iter,
+ &area.y,
+ &area.height);
+ area.x = start;
+ area.width = width;
+
+ path = g_strdup_printf ("%d", line);
+
+ ret = gtk_cell_renderer_activate (renderer->renderer,
+ (GdkEvent *)event,
+ GTK_WIDGET (gutter->priv->view),
+ path,
+ &area,
+ &area,
+ 0);
+
+ g_signal_emit (gutter,
+ signals[CELL_ACTIVATED],
+ 0,
+ renderer->renderer,
+ &line_iter,
+ (GdkEvent *)event);
+
+ g_free (path);
+ do_redraw (gutter);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+on_view_query_tooltip (GtkSourceView *view,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ GtkSourceGutter *gutter)
+{
+ GtkTextView *text_view = GTK_TEXT_VIEW (view);
+ Renderer *renderer;
+ gint start = 0;
+ gint width = 0;
+ gint y_buf;
+ gint yline;
+ GtkTextIter line_iter;
+ gboolean ret;
+
+ if (keyboard_mode)
+ {
+ return FALSE;
+ }
+
+ /* Check cell renderer */
+ renderer = renderer_at_x (gutter, x, &start, &width);
+
+ if (!renderer)
+ {
+ return FALSE;
+ }
+
+ gtk_text_view_window_to_buffer_coords (text_view,
+ gutter->priv->window_type,
+ x, y,
+ NULL, &y_buf);
+
+ gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view),
+ &line_iter,
+ y_buf,
+ &yline);
+
+ if (yline > y_buf)
+ {
+ return FALSE;
+ }
+
+ g_signal_emit (gutter,
+ signals[QUERY_TOOLTIP],
+ 0,
+ renderer->renderer,
+ &line_iter,
+ tooltip,
+ &ret);
+
+ return ret;
+}
+
+/* vi:ts=8 */
diff --git a/gtksourceview/gtksourcegutter.h b/gtksourceview/gtksourcegutter.h
new file mode 100644
index 0000000..32c57a9
--- /dev/null
+++ b/gtksourceview/gtksourcegutter.h
@@ -0,0 +1,108 @@
+/*
+ * gtksourcegutter.h
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+
+#ifndef __GTK_SOURCE_GUTTER_H__
+#define __GTK_SOURCE_GUTTER_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_GUTTER (gtk_source_gutter_get_type ())
+#define GTK_SOURCE_GUTTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_GUTTER, GtkSourceGutter))
+#define GTK_SOURCE_GUTTER_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_GUTTER, GtkSourceGutter const))
+#define GTK_SOURCE_GUTTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SOURCE_GUTTER, GtkSourceGutterClass))
+#define GTK_IS_SOURCE_GUTTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_GUTTER))
+#define GTK_IS_SOURCE_GUTTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_GUTTER))
+#define GTK_SOURCE_GUTTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_GUTTER, GtkSourceGutterClass))
+
+typedef struct _GtkSourceGutter GtkSourceGutter;
+typedef struct _GtkSourceGutterClass GtkSourceGutterClass;
+typedef struct _GtkSourceGutterPrivate GtkSourceGutterPrivate;
+
+struct _GtkSourceGutter {
+ GObject parent;
+
+ GtkSourceGutterPrivate *priv;
+};
+
+struct _GtkSourceGutterClass {
+ GObjectClass parent_class;
+
+ void (*cell_activated) (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkTextIter *iter,
+ GdkEvent *event);
+
+ gboolean (*query_tooltip) (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkTextIter *iter,
+ GtkTooltip *tooltip);
+};
+
+typedef void (*GtkSourceGutterDataFunc) (GtkSourceGutter *gutter,
+ GtkCellRenderer *cell,
+ gint line_number,
+ gboolean current_line,
+ gpointer data);
+
+typedef void (*GtkSourceGutterSizeFunc) (GtkSourceGutter *gutter,
+ GtkCellRenderer *cell,
+ gpointer data);
+
+GType gtk_source_gutter_get_type (void) G_GNUC_CONST;
+
+GdkWindow *gtk_source_gutter_get_window (GtkSourceGutter *gutter);
+
+void gtk_source_gutter_insert (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ gint position);
+
+void gtk_source_gutter_reorder (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ gint position);
+
+void gtk_source_gutter_remove (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer);
+
+void gtk_source_gutter_set_cell_data_func (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkSourceGutterDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy);
+
+void gtk_source_gutter_set_cell_size_func (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkSourceGutterSizeFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy);
+
+void gtk_source_gutter_queue_draw (GtkSourceGutter *gutter);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_GUTTER_H__ */
+
+/* vi:ts=8 */
diff --git a/gtksourceview/gtksourceview-marshal.list b/gtksourceview/gtksourceview-marshal.list
index 93e1e2a..5518236 100644
--- a/gtksourceview/gtksourceview-marshal.list
+++ b/gtksourceview/gtksourceview-marshal.list
@@ -4,3 +4,6 @@ VOID:BOXED
VOID:BOXED,BOXED
VOID:INT,INT
VOID:STRING
+VOID:OBJECT,BOXED,POINTER
+BOOL:OBJECT,BOXED,OBJECT
+VOID:BOXED,POINTER
diff --git a/gtksourceview/gtksourceview.c b/gtksourceview/gtksourceview.c
index 67878bf..40da9c8 100644
--- a/gtksourceview/gtksourceview.c
+++ b/gtksourceview/gtksourceview.c
@@ -39,6 +39,7 @@
#include "gtksourceview-typebuiltins.h"
#include "gtksourcemark.h"
#include "gtksourceview.h"
+#include "gtksourcegutter-private.h"
/*
#define ENABLE_DEBUG
@@ -65,7 +66,6 @@
#define COMPOSITE_ALPHA 225
#define GUTTER_PIXMAP 16
#define DEFAULT_TAB_WIDTH 8
-#define MIN_NUMBER_WINDOW_WIDTH 20
#define MAX_TAB_WIDTH 32
#define MAX_INDENT_WIDTH 32
@@ -79,6 +79,7 @@
enum {
UNDO,
REDO,
+ LINE_MARK_ACTIVATED,
LAST_SIGNAL
};
@@ -115,7 +116,6 @@ struct _GtkSourceViewPrivate
gboolean show_right_margin;
guint right_margin_pos;
gint cached_right_margin_pos;
- gint cached_line_number_width;
gboolean style_scheme_applied;
GtkSourceStyleScheme *style_scheme;
@@ -128,10 +128,15 @@ struct _GtkSourceViewPrivate
GHashTable *mark_categories;
GtkSourceBuffer *source_buffer;
- gint old_lines;
GdkColor current_line_color;
guint current_line_color_set : 1;
+
+ GtkSourceGutter *left_gutter;
+ GtkSourceGutter *right_gutter;
+
+ GtkCellRenderer *line_renderer;
+ GtkCellRenderer *marks_renderer;
};
@@ -153,11 +158,11 @@ typedef struct
{
gint priority;
GdkPixbuf *pixbuf;
-
+
GtkSourceViewMarkTooltipFunc tooltip_func;
gpointer tooltip_data;
GDestroyNotify tooltip_data_notify;
-
+
GdkColor background;
guint background_set : 1;
guint tooltip_markup : 1;
@@ -197,8 +202,6 @@ static gint gtk_source_view_expose (GtkWidget *widget,
GdkEventExpose *event);
static gboolean gtk_source_view_key_press_event (GtkWidget *widget,
GdkEventKey *event);
-static gboolean gtk_source_view_button_press_event (GtkWidget *widget,
- GdkEventButton *event);
static void view_dnd_drop (GtkTextView *view,
GdkDragContext *context,
gint x,
@@ -224,11 +227,6 @@ static void gtk_source_view_get_property (GObject *object,
static void gtk_source_view_style_set (GtkWidget *widget,
GtkStyle *previous_style);
static void gtk_source_view_realize (GtkWidget *widget);
-static gboolean gtk_source_view_query_tooltip (GtkWidget *widget,
- gint x,
- gint y,
- gboolean keyboard_mode,
- GtkTooltip *tooltip);
static void gtk_source_view_update_style_scheme (GtkSourceView *view);
static MarkCategory *
@@ -262,11 +260,9 @@ gtk_source_view_class_init (GtkSourceViewClass *klass)
object_class->set_property = gtk_source_view_set_property;
widget_class->key_press_event = gtk_source_view_key_press_event;
- widget_class->button_press_event = gtk_source_view_button_press_event;
widget_class->expose_event = gtk_source_view_expose;
widget_class->style_set = gtk_source_view_style_set;
widget_class->realize = gtk_source_view_realize;
- widget_class->query_tooltip = gtk_source_view_query_tooltip;
textview_class->populate_popup = gtk_source_view_populate_popup;
textview_class->move_cursor = gtk_source_view_move_cursor;
@@ -444,6 +440,29 @@ gtk_source_view_class_init (GtkSourceViewClass *klass)
G_TYPE_NONE,
0);
+ /**
+ * GtkSourceView::line-mark-activated:
+ * @view: the #GtkSourceView
+ * @iter: a #GtkTextIter
+ * @event: the #GdkEvent that activated the event
+ *
+ * Emitted when a line mark has been activated (for instance when there
+ * was a button press in the line marks gutter). You can use @iter to
+ * determine on which line the activation took place.
+ */
+ signals [LINE_MARK_ACTIVATED] =
+ g_signal_new ("line-mark-activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkSourceViewClass, line_mark_activated),
+ NULL,
+ NULL,
+ _gtksourceview_marshal_VOID__BOXED_POINTER,
+ G_TYPE_NONE,
+ 2,
+ GTK_TYPE_TEXT_ITER,
+ G_TYPE_POINTER);
+
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set,
@@ -716,6 +735,443 @@ notify_buffer (GtkSourceView *view)
set_source_buffer (view, GTK_TEXT_VIEW (view)->buffer);
}
+static gint
+sort_marks_by_priority (gconstpointer m1,
+ gconstpointer m2,
+ gpointer data)
+{
+ GtkSourceMark *mark1 = GTK_SOURCE_MARK (m1);
+ GtkSourceMark *mark2 = GTK_SOURCE_MARK (m2);
+ GtkSourceView *view = GTK_SOURCE_VIEW (data);
+ GtkTextIter iter1, iter2;
+ gint line1;
+ gint line2;
+
+ gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark1)),
+ &iter1,
+ GTK_TEXT_MARK (mark1));
+ gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark2)),
+ &iter2,
+ GTK_TEXT_MARK (mark2));
+
+ line1 = gtk_text_iter_get_line (&iter1);
+ line2 = gtk_text_iter_get_line (&iter2);
+
+ if (line1 == line2)
+ {
+ guint priority1 = gtk_source_view_get_mark_category_priority (view,
+ gtk_source_mark_get_category (mark1));
+ guint priority2 = gtk_source_view_get_mark_category_priority (view,
+ gtk_source_mark_get_category (mark2));
+
+ return priority1 - priority2;
+ }
+ else
+ {
+ return line2 - line1;
+ }
+}
+
+static GdkPixbuf *
+composite_marks (GtkSourceView *view,
+ GSList *marks)
+{
+ GdkPixbuf *composite;
+ gint mark_width, mark_height;
+
+ /* Draw the mark with higher priority */
+ marks = g_slist_sort_with_data (marks, sort_marks_by_priority, view);
+
+ composite = NULL;
+ mark_width = mark_height = 0;
+
+ /* composite all the pixbufs for the marks present at the line */
+ do
+ {
+ GtkSourceMark *mark;
+ GdkPixbuf *pixbuf;
+
+ mark = marks->data;
+
+ pixbuf = gtk_source_view_get_mark_category_pixbuf (view,
+ gtk_source_mark_get_category (mark));
+
+ if (pixbuf != NULL)
+ {
+ if (composite == NULL)
+ {
+ composite = gdk_pixbuf_copy (pixbuf);
+ mark_width = gdk_pixbuf_get_width (composite);
+ mark_height = gdk_pixbuf_get_height (composite);
+ }
+ else
+ {
+ gint pixbuf_w;
+ gint pixbuf_h;
+
+ pixbuf_w = gdk_pixbuf_get_width (pixbuf);
+ pixbuf_h = gdk_pixbuf_get_height (pixbuf);
+
+ gdk_pixbuf_composite (pixbuf,
+ composite,
+ 0, 0,
+ mark_width, mark_height,
+ 0, 0,
+ (double) pixbuf_w / mark_width,
+ (double) pixbuf_h / mark_height,
+ GDK_INTERP_BILINEAR,
+ COMPOSITE_ALPHA);
+ }
+
+ g_object_unref (pixbuf);
+ }
+
+ marks = g_slist_next (marks);
+ }
+ while (marks);
+
+ return composite;
+}
+
+static void
+marks_renderer_data_func (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ gint line_number,
+ gboolean current_line,
+ GtkSourceView *view)
+{
+ GSList *marks;
+ GdkPixbuf *pixbuf = NULL;
+
+ if (view->priv->source_buffer)
+ {
+ marks = gtk_source_buffer_get_source_marks_at_line (view->priv->source_buffer,
+ line_number,
+ NULL);
+
+ if (marks != NULL)
+ {
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (view->priv->source_buffer),
+ &iter,
+ line_number);
+
+ /* draw marks for the line */
+ pixbuf = composite_marks (view, marks);
+ g_slist_free (marks);
+ }
+ }
+
+ g_object_set (G_OBJECT (renderer),
+ "pixbuf", pixbuf,
+ "xpad", 2,
+ "yalign", 0.0,
+ "xalign", 0.5,
+ "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+ NULL);
+}
+
+static void
+line_renderer_data_func (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ gint line_number,
+ gboolean current_line,
+ GtkSourceView *view)
+{
+ gchar *text;
+
+ text = g_strdup_printf ("%d", line_number + 1);
+ g_object_set (G_OBJECT (renderer),
+ "text", text,
+ "xalign", 1.0,
+ "yalign", 0.0,
+ "xpad", 2,
+ "ypad", 0,
+ "weight", current_line ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
+ "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+ NULL);
+
+ GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view));
+
+ if (style)
+ {
+ g_object_set (G_OBJECT (renderer),
+ "foreground-gdk", &style->fg[GTK_STATE_NORMAL],
+ NULL);
+ }
+
+ g_object_set (G_OBJECT (renderer),
+ "background-set", FALSE,
+ NULL);
+
+ g_free (text);
+}
+
+static void
+line_renderer_size_func (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkSourceView *view)
+{
+ gchar *markup;
+ gint count;
+
+ count = gtk_text_buffer_get_line_count (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+ markup = g_strdup_printf ("<b>%d</b>", MAX(99, count));
+
+ g_object_set (G_OBJECT (renderer),
+ "markup", markup,
+ "xpad", 2,
+ "ypad", 0,
+ NULL);
+ g_free (markup);
+}
+
+static void
+extend_selection_to_line (GtkTextBuffer *buf, GtkTextIter *line_start)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+ GtkTextIter line_end;
+
+ gtk_text_buffer_get_selection_bounds (buf, &start, &end);
+
+ line_end = *line_start;
+ gtk_text_iter_forward_to_line_end (&line_end);
+
+ if (gtk_text_iter_compare (&start, line_start) < 0)
+ {
+ gtk_text_buffer_select_range (buf, &start, &line_end);
+ }
+ else if (gtk_text_iter_compare (&end, &line_end) < 0)
+ {
+ /* if the selection is in this line, extend
+ * the selection to the whole line */
+ gtk_text_buffer_select_range (buf, &line_end, line_start);
+ }
+ else
+ {
+ gtk_text_buffer_select_range (buf, &end, line_start);
+ }
+}
+
+static void
+select_line (GtkTextBuffer *buf, GtkTextIter *line_start)
+{
+ GtkTextIter iter;
+
+ iter = *line_start;
+
+ if (!gtk_text_iter_ends_line (&iter))
+ gtk_text_iter_forward_to_line_end (&iter);
+
+ /* Select the line, put the cursor at the end of the line */
+ gtk_text_buffer_select_range (buf, &iter, line_start);
+}
+
+static void
+renderer_activated (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkTextIter *iter,
+ GdkEvent *event,
+ GtkSourceView *view)
+{
+ if (renderer == view->priv->marks_renderer)
+ {
+ g_signal_emit (view,
+ signals[LINE_MARK_ACTIVATED],
+ 0,
+ iter,
+ event);
+ }
+ else if (renderer == view->priv->line_renderer)
+ {
+ GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ if (event->type == GDK_BUTTON_PRESS && (event->button.button == 1))
+ {
+ if ((event->button.state & GDK_CONTROL_MASK) != 0)
+ {
+ /* Single click + Ctrl -> select the line */
+ select_line (buf, iter);
+ }
+ else if ((event->button.state & GDK_SHIFT_MASK) != 0)
+ {
+ /* Single click + Shift -> extended current
+ selection to include the clicked line */
+ extend_selection_to_line (buf, iter);
+ }
+ else
+ {
+ gtk_text_buffer_place_cursor (buf, iter);
+ }
+ }
+ else if (event->type == GDK_2BUTTON_PRESS && (event->button.button == 1))
+ {
+ select_line (buf, iter);
+ }
+ }
+}
+
+static void
+set_tooltip_widget_from_marks (GtkSourceView *view,
+ GtkTooltip *tooltip,
+ GSList *marks)
+{
+ GtkWidget *vbox;
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_widget_show (vbox);
+
+ while (marks != NULL)
+ {
+ MarkCategory *cat;
+ GtkSourceMark *mark;
+
+ mark = marks->data;
+ cat = gtk_source_view_get_mark_category (view, mark);
+
+ if (cat != NULL)
+ {
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *hbox;
+ GtkWidget *separator;
+ gchar *text;
+
+ hbox = gtk_hbox_new (FALSE, 4);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox,
+ FALSE, FALSE, 0);
+
+ text = cat->tooltip_func (mark, cat->tooltip_data);
+
+ if (text != NULL)
+ {
+ label = gtk_label_new (NULL);
+ if (cat->tooltip_markup)
+ gtk_label_set_markup (GTK_LABEL (label), text);
+ else
+ gtk_label_set_text (GTK_LABEL (label), text);
+
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_show (label);
+
+ gtk_box_pack_end (GTK_BOX (hbox), label,
+ TRUE, TRUE, 0);
+
+ if (cat->pixbuf != NULL)
+ {
+ image = gtk_image_new_from_pixbuf (cat->pixbuf);
+ gtk_widget_show (image);
+
+ gtk_box_pack_start (GTK_BOX (hbox), image,
+ FALSE, FALSE, 0);
+ }
+
+ if (g_slist_length (marks) != 1)
+ {
+ separator = gtk_hseparator_new ();
+ gtk_widget_show (separator);
+ gtk_box_pack_start (GTK_BOX (vbox), separator,
+ FALSE, FALSE, 0);
+ }
+
+ g_free (text);
+ }
+ }
+
+ marks = g_slist_delete_link (marks, marks);
+ }
+
+ gtk_tooltip_set_custom (tooltip, vbox);
+}
+
+static gboolean
+renderer_query_tooltip (GtkSourceGutter *gutter,
+ GtkCellRenderer *renderer,
+ GtkTextIter *iter,
+ GtkTooltip *tooltip,
+ GtkSourceView *view)
+{
+ GSList *marks;
+ GtkSourceBuffer *buffer;
+ gint line;
+
+ if (renderer != view->priv->marks_renderer)
+ {
+ return FALSE;
+ }
+
+ buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+ line = gtk_text_iter_get_line (iter);
+
+ marks = gtk_source_buffer_get_source_marks_at_line (buffer,
+ line,
+ NULL);
+
+ if (marks != NULL)
+ {
+ marks = g_slist_sort_with_data (marks, sort_marks_by_priority, view);
+ marks = g_slist_reverse (marks);
+
+ set_tooltip_widget_from_marks (view, tooltip, marks);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+init_left_gutter (GtkSourceView *view)
+{
+ GtkSourceGutter *gutter;
+
+ view->priv->line_renderer = gtk_cell_renderer_text_new ();
+ view->priv->marks_renderer = gtk_cell_renderer_pixbuf_new ();
+
+ gutter = gtk_source_view_get_gutter (view, GTK_TEXT_WINDOW_LEFT);
+
+ gtk_source_gutter_insert (gutter,
+ view->priv->line_renderer,
+ GTK_SOURCE_VIEW_GUTTER_POSITION_LINES);
+
+ gtk_source_gutter_insert (gutter,
+ view->priv->marks_renderer,
+ GTK_SOURCE_VIEW_GUTTER_POSITION_MARKS);
+
+ gtk_cell_renderer_set_fixed_size (view->priv->line_renderer, 0, 0);
+ gtk_cell_renderer_set_fixed_size (view->priv->marks_renderer, 0, 0);
+
+ gtk_source_gutter_set_cell_data_func (gutter,
+ view->priv->line_renderer,
+ (GtkSourceGutterDataFunc)line_renderer_data_func,
+ view,
+ NULL);
+
+ gtk_source_gutter_set_cell_size_func (gutter,
+ view->priv->line_renderer,
+ (GtkSourceGutterSizeFunc)line_renderer_size_func,
+ view,
+ NULL);
+
+ gtk_source_gutter_set_cell_data_func (gutter,
+ view->priv->marks_renderer,
+ (GtkSourceGutterDataFunc)marks_renderer_data_func,
+ view,
+ NULL);
+
+ g_signal_connect (gutter,
+ "cell-activated",
+ G_CALLBACK (renderer_activated),
+ view);
+
+ g_signal_connect (gutter,
+ "query-tooltip",
+ G_CALLBACK (renderer_query_tooltip),
+ view);
+}
+
static void
gtk_source_view_init (GtkSourceView *view)
{
@@ -731,7 +1187,6 @@ gtk_source_view_init (GtkSourceView *view)
view->priv->smart_home_end = GTK_SOURCE_SMART_HOME_END_DISABLED;
view->priv->right_margin_pos = DEFAULT_RIGHT_MARGIN_POSITION;
view->priv->cached_right_margin_pos = -1;
- view->priv->cached_line_number_width = 0;
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 2);
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view), 2);
@@ -744,6 +1199,8 @@ gtk_source_view_init (GtkSourceView *view)
(GDestroyNotify) g_free,
(GDestroyNotify) mark_category_free);
+ init_left_gutter (view);
+
tl = gtk_drag_dest_get_target_list (GTK_WIDGET (view));
g_return_if_fail (tl != NULL);
@@ -796,13 +1253,19 @@ gtk_source_view_finalize (GObject *object)
if (view->priv->right_margin_overlay_color != NULL)
gdk_color_free (view->priv->right_margin_overlay_color);
-
+
if (view->priv->spaces_color != NULL)
gdk_color_free (view->priv->spaces_color);
if (view->priv->mark_categories)
g_hash_table_destroy (view->priv->mark_categories);
+ if (view->priv->left_gutter)
+ g_object_unref (view->priv->left_gutter);
+
+ if (view->priv->right_gutter)
+ g_object_unref (view->priv->right_gutter);
+
set_source_buffer (view, NULL);
G_OBJECT_CLASS (gtk_source_view_parent_class)->finalize (object);
@@ -1254,314 +1717,6 @@ gtk_source_view_get_lines (GtkTextView *text_view,
*countp = count;
}
-static gint
-sort_marks_by_priority (gconstpointer m1,
- gconstpointer m2,
- gpointer data)
-{
- GtkSourceMark *mark1 = GTK_SOURCE_MARK (m1);
- GtkSourceMark *mark2 = GTK_SOURCE_MARK (m2);
- GtkSourceView *view = GTK_SOURCE_VIEW (data);
- GtkTextIter iter1, iter2;
- gint line1;
- gint line2;
-
- gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark1)),
- &iter1,
- GTK_TEXT_MARK (mark1));
- gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark2)),
- &iter2,
- GTK_TEXT_MARK (mark2));
-
- line1 = gtk_text_iter_get_line (&iter1);
- line2 = gtk_text_iter_get_line (&iter2);
-
- if (line1 == line2)
- {
- guint priority1 = gtk_source_view_get_mark_category_priority (view,
- gtk_source_mark_get_category (mark1));
- guint priority2 = gtk_source_view_get_mark_category_priority (view,
- gtk_source_mark_get_category (mark2));
-
- return priority1 - priority2;
- }
- else
- {
- return line2 - line1;
- }
-}
-
-static void
-draw_line_marks (GtkSourceView *view,
- GSList *marks,
- gint x,
- gint y,
- gint height)
-{
- GdkPixbuf *composite;
- gint mark_width, mark_height;
-
- /* Draw the mark with higher priority */
- marks = g_slist_sort_with_data (marks, sort_marks_by_priority, view);
-
- composite = NULL;
- mark_width = mark_height = 0;
-
- /* composite all the pixbufs for the marks present at the line */
- do
- {
- GtkSourceMark *mark;
- GdkPixbuf *pixbuf;
- MarkCategory *cat;
-
- mark = marks->data;
-
- cat = gtk_source_view_get_mark_category (view, mark);
- if (cat != NULL && cat->pixbuf != NULL)
- pixbuf = g_object_ref (cat->pixbuf);
-
- if (pixbuf != NULL)
- {
- if (composite == NULL)
- {
- composite = gdk_pixbuf_copy (pixbuf);
- mark_width = gdk_pixbuf_get_width (composite);
- mark_height = gdk_pixbuf_get_height (composite);
- }
- else
- {
- gint pixbuf_w;
- gint pixbuf_h;
-
- pixbuf_w = gdk_pixbuf_get_width (pixbuf);
- pixbuf_h = gdk_pixbuf_get_height (pixbuf);
- gdk_pixbuf_composite (pixbuf,
- composite,
- 0, 0,
- mark_width, mark_height,
- 0, 0,
- (double) pixbuf_w / mark_width,
- (double) pixbuf_h / mark_height,
- GDK_INTERP_BILINEAR,
- COMPOSITE_ALPHA);
- }
- g_object_unref (pixbuf);
- }
-
- marks = g_slist_next (marks);
- }
- while (marks);
-
- if (composite != NULL)
- {
- GdkWindow *window;
-
- window = gtk_text_view_get_window (GTK_TEXT_VIEW (view),
- GTK_TEXT_WINDOW_LEFT);
-
- gdk_draw_pixbuf (GDK_DRAWABLE (window), NULL, composite,
- 0, 0, x, y + (height - mark_height) / 2.0,
- mark_width, mark_height,
- GDK_RGB_DITHER_NORMAL, 0, 0);
- g_object_unref (composite);
- }
-}
-
-static void
-gtk_source_view_paint_margin (GtkSourceView *view,
- GdkEventExpose *event)
-{
- GtkTextView *text_view;
- GdkWindow *win;
- PangoLayout *layout;
- GArray *numbers;
- GArray *pixels;
- gchar str [8]; /* we don't expect more than ten million lines ;-) */
- gint y1, y2;
- gint count;
- gint margin_width;
- gint text_width, x_pixmap;
- gint i;
- GtkTextIter cur;
- gint cur_line;
-
- text_view = GTK_TEXT_VIEW (view);
-
- if (!view->priv->show_line_numbers && !view->priv->show_line_marks)
- {
- gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
- GTK_TEXT_WINDOW_LEFT,
- 0);
-
- return;
- }
-
- win = gtk_text_view_get_window (text_view,
- GTK_TEXT_WINDOW_LEFT);
-
- y1 = event->area.y;
- y2 = y1 + event->area.height;
-
- /* get the extents of the line printing */
- gtk_text_view_window_to_buffer_coords (text_view,
- GTK_TEXT_WINDOW_LEFT,
- 0,
- y1,
- NULL,
- &y1);
-
- gtk_text_view_window_to_buffer_coords (text_view,
- GTK_TEXT_WINDOW_LEFT,
- 0,
- y2,
- NULL,
- &y2);
-
- numbers = g_array_new (FALSE, FALSE, sizeof (gint));
- pixels = g_array_new (FALSE, FALSE, sizeof (gint));
-
- /* get the line numbers and y coordinates. */
- gtk_source_view_get_lines (text_view,
- y1,
- y2,
- pixels,
- NULL,
- numbers,
- &count);
-
- /* A zero-lined document should display a "1"; we don't need to worry about
- scrolling effects of the text widget in this special case */
-
- if (count == 0)
- {
- gint y = 0;
- gint n = 0;
- count = 1;
- g_array_append_val (pixels, y);
- g_array_append_val (numbers, n);
- }
-
- DEBUG ({
- g_message ("Painting line numbers %d - %d",
- g_array_index (numbers, gint, 0),
- g_array_index (numbers, gint, count - 1));
- });
-
- /* set size. */
- g_snprintf (str, sizeof (str),
- "%d", MAX (99, gtk_text_buffer_get_line_count (text_view->buffer)));
- layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), str);
-
- pango_layout_get_pixel_size (layout, &text_width, NULL);
-
- pango_layout_set_width (layout, text_width);
- pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
-
- /* determine the width of the left margin. */
- if (view->priv->show_line_numbers)
- margin_width = text_width + 4;
- else
- margin_width = 0;
- view->priv->cached_line_number_width = margin_width;
-
- x_pixmap = margin_width;
-
- if (view->priv->show_line_marks)
- margin_width += GUTTER_PIXMAP;
-
- /* no line & no marks case is short circuited before */
- g_assert (margin_width != 0);
-
- gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
- GTK_TEXT_WINDOW_LEFT,
- margin_width);
-
- gtk_text_buffer_get_iter_at_mark (text_view->buffer,
- &cur,
- gtk_text_buffer_get_insert (text_view->buffer));
-
- cur_line = gtk_text_iter_get_line (&cur);
-
- for (i = 0; i < count; ++i)
- {
- gint pos;
- gint line_to_paint;
-
- gtk_text_view_buffer_to_window_coords (text_view,
- GTK_TEXT_WINDOW_LEFT,
- 0,
- g_array_index (pixels, gint, i),
- NULL,
- &pos);
-
- line_to_paint = g_array_index (numbers, gint, i);
-
- if (view->priv->show_line_numbers)
- {
- if (line_to_paint == cur_line)
- {
- gchar *markup;
-
- /* +1 because displayed line numbers start from 1 */
- markup = g_strdup_printf ("<b>%d</b>", 1 + line_to_paint);
-
- pango_layout_set_markup (layout, markup, -1);
-
- g_free (markup);
- }
- else
- {
- g_snprintf (str, sizeof (str), "%d", 1 + line_to_paint);
-
- pango_layout_set_markup (layout, str, -1);
- }
-
- gtk_paint_layout (GTK_WIDGET (view)->style,
- win,
- GTK_WIDGET_STATE (view),
- FALSE,
- NULL,
- GTK_WIDGET (view),
- NULL,
- text_width + 2,
- pos,
- layout);
- }
-
- if (view->priv->show_line_marks && view->priv->source_buffer != NULL)
- {
- GSList *marks;
-
- marks = gtk_source_buffer_get_source_marks_at_line (view->priv->source_buffer,
- line_to_paint,
- NULL);
-
- if (marks != NULL)
- {
- GtkTextIter iter;
- gint height;
-
- gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (view->priv->source_buffer),
- &iter,
- line_to_paint);
- gtk_text_view_get_line_yrange (GTK_TEXT_VIEW (view),
- &iter,
- NULL,
- &height);
-
- /* draw marks for the line */
- draw_line_marks (view, marks, x_pixmap, pos, height);
- g_slist_free (marks);
- }
- }
- }
-
- g_array_free (pixels, TRUE);
- g_array_free (numbers, TRUE);
-
- g_object_unref (G_OBJECT (layout));
-}
-
static void
gtk_source_view_paint_line_background (GtkTextView *text_view,
GdkEventExpose *event,
@@ -1957,6 +2112,104 @@ draw_tabs_and_spaces (GtkSourceView *view,
cairo_destroy (cr);
}
+static void
+gtk_source_view_paint_right_margin (GtkSourceView *view,
+ GdkEventExpose *event)
+{
+ GdkRectangle visible_rect;
+ GdkRectangle redraw_rect;
+ cairo_t *cr;
+ double x;
+
+ GtkTextView *text_view = GTK_TEXT_VIEW (view);
+
+#ifdef ENABLE_PROFILE
+ static GTimer *timer = NULL;
+#endif
+
+ g_return_if_fail (view->priv->right_margin_line_color != NULL);
+
+ if (view->priv->cached_right_margin_pos < 0)
+ {
+ view->priv->cached_right_margin_pos =
+ calculate_real_tab_width (view,
+ view->priv->right_margin_pos,
+ '_');
+ }
+
+#ifdef ENABLE_PROFILE
+ if (timer == NULL)
+ timer = g_timer_new ();
+
+ g_timer_start (timer);
+#endif
+
+ gtk_text_view_get_visible_rect (text_view, &visible_rect);
+
+ gtk_text_view_buffer_to_window_coords (text_view,
+ GTK_TEXT_WINDOW_TEXT,
+ visible_rect.x,
+ visible_rect.y,
+ &redraw_rect.x,
+ &redraw_rect.y);
+
+ redraw_rect.width = visible_rect.width;
+ redraw_rect.height = visible_rect.height;
+
+ cr = gdk_cairo_create (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT));
+
+ /* Set a clip region for the expose event. */
+ cairo_rectangle (cr, event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ cairo_clip (cr);
+
+ /* Offset with 0.5 is needed for a sharp line. */
+ x = view->priv->cached_right_margin_pos -
+ visible_rect.x + redraw_rect.x + 0.5 +
+ gtk_text_view_get_left_margin (text_view);
+
+ /* Default line width is 2.0 which is too wide. */
+ cairo_set_line_width (cr, 1.0);
+
+ cairo_move_to (cr, x, redraw_rect.y);
+ cairo_line_to (cr, x, redraw_rect.y + redraw_rect.height);
+
+ cairo_set_source_rgba (cr,
+ view->priv->right_margin_line_color->red / 65535.,
+ view->priv->right_margin_line_color->green / 65535.,
+ view->priv->right_margin_line_color->blue / 65535.,
+ RIGHT_MARING_LINE_ALPHA / 255.);
+
+ cairo_stroke (cr);
+
+ /* Only draw the overlay when the style scheme explicitly sets it. */
+ if (view->priv->right_margin_overlay_color != NULL)
+ {
+ /* Draw the rectangle next to the line (x+.5). */
+ cairo_rectangle (cr,
+ x + .5,
+ redraw_rect.y,
+ redraw_rect.width - x - .5,
+ redraw_rect.y + redraw_rect.height);
+
+ cairo_set_source_rgba (cr,
+ view->priv->right_margin_overlay_color->red / 65535.,
+ view->priv->right_margin_overlay_color->green / 65535.,
+ view->priv->right_margin_overlay_color->blue / 65535.,
+ RIGHT_MARING_OVERLAY_ALPHA / 255.);
+
+ cairo_fill (cr);
+ }
+
+ cairo_destroy (cr);
+
+ PROFILE ({
+ g_timer_stop (timer);
+ g_message ("Time to draw the margin: %g (sec * 1000)",
+ g_timer_elapsed (timer, NULL) * 1000);
+ });
+}
+
static gint
gtk_source_view_expose (GtkWidget *widget,
GdkEventExpose *event)
@@ -2003,161 +2256,45 @@ gtk_source_view_expose (GtkWidget *widget,
&iter1, &iter2, FALSE);
}
- /* now check for the left window, which contains the margin */
- if (event->window == gtk_text_view_get_window (text_view,
- GTK_TEXT_WINDOW_LEFT))
- {
- gtk_source_view_paint_margin (view, event);
- event_handled = TRUE;
- }
- else
- {
- gint lines;
-
- /* FIXME: could it be a performances problem? - Paolo */
- lines = gtk_text_buffer_get_line_count (text_view->buffer);
-
- if (view->priv->old_lines != lines)
- {
- GdkWindow *w;
- view->priv->old_lines = lines;
-
- w = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_LEFT);
-
- if (w != NULL)
- gdk_window_invalidate_rect (w, NULL, FALSE);
- }
-
- if (GTK_WIDGET_IS_SENSITIVE(view) && view->priv->highlight_current_line &&
+ if (GTK_WIDGET_IS_SENSITIVE(view) && view->priv->highlight_current_line &&
(event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)))
- {
- GtkTextIter cur;
- gint y, height;
- GdkColor *color;
-
- gtk_text_buffer_get_iter_at_mark (text_view->buffer,
- &cur,
- gtk_text_buffer_get_insert (text_view->buffer));
- gtk_text_view_get_line_yrange (text_view, &cur, &y, &height);
-
- if (view->priv->current_line_color_set)
- color = &view->priv->current_line_color;
- else
- color = &widget->style->bg[GTK_WIDGET_STATE (widget)];
-
- gtk_source_view_paint_line_background (text_view, event, y, height, color);
- }
-
- if (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT))
- gtk_source_view_paint_marks_background (view, event);
-
- /* Have GtkTextView draw the text first. */
- if (GTK_WIDGET_CLASS (gtk_source_view_parent_class)->expose_event)
- event_handled =
- GTK_WIDGET_CLASS (gtk_source_view_parent_class)->expose_event (widget, event);
-
- /* Draw the right margin vertical line + overlay. */
- if (view->priv->show_right_margin &&
- (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)))
- {
- GdkRectangle visible_rect;
- GdkRectangle redraw_rect;
- cairo_t *cr;
- double x;
-
-#ifdef ENABLE_PROFILE
- static GTimer *timer = NULL;
-#endif
-
- g_return_val_if_fail (view->priv->right_margin_line_color != NULL,
- event_handled);
-
- if (view->priv->cached_right_margin_pos < 0)
- {
- view->priv->cached_right_margin_pos =
- calculate_real_tab_width (view,
- view->priv->right_margin_pos,
- '_');
- }
-
-#ifdef ENABLE_PROFILE
- if (timer == NULL)
- timer = g_timer_new ();
-
- g_timer_start (timer);
-#endif
-
- gtk_text_view_get_visible_rect (text_view, &visible_rect);
-
- gtk_text_view_buffer_to_window_coords (text_view,
- GTK_TEXT_WINDOW_TEXT,
- visible_rect.x,
- visible_rect.y,
- &redraw_rect.x,
- &redraw_rect.y);
-
- redraw_rect.width = visible_rect.width;
- redraw_rect.height = visible_rect.height;
-
- cr = gdk_cairo_create (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT));
-
- /* Set a clip region for the expose event. */
- cairo_rectangle (cr, event->area.x, event->area.y,
- event->area.width, event->area.height);
- cairo_clip (cr);
-
- /* Offset with 0.5 is needed for a sharp line. */
- x = view->priv->cached_right_margin_pos -
- visible_rect.x + redraw_rect.x + 0.5 +
- gtk_text_view_get_left_margin (text_view);
-
- /* Default line width is 2.0 which is too wide. */
- cairo_set_line_width (cr, 1.0);
+ {
+ GtkTextIter cur;
+ gint y, height;
+ GdkColor *color;
- cairo_move_to (cr, x, redraw_rect.y);
- cairo_line_to (cr, x, redraw_rect.y + redraw_rect.height);
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer,
+ &cur,
+ gtk_text_buffer_get_insert (text_view->buffer));
+ gtk_text_view_get_line_yrange (text_view, &cur, &y, &height);
- cairo_set_source_rgba (cr,
- view->priv->right_margin_line_color->red / 65535.,
- view->priv->right_margin_line_color->green / 65535.,
- view->priv->right_margin_line_color->blue / 65535.,
- RIGHT_MARING_LINE_ALPHA / 255.);
+ if (view->priv->current_line_color_set)
+ color = &view->priv->current_line_color;
+ else
+ color = &widget->style->bg[GTK_WIDGET_STATE (widget)];
- cairo_stroke (cr);
+ gtk_source_view_paint_line_background (text_view, event, y, height, color);
+ }
- /* Only draw the overlay when the style scheme explicitly sets it. */
- if (view->priv->right_margin_overlay_color != NULL)
- {
- /* Draw the rectangle next to the line (x+.5). */
- cairo_rectangle (cr,
- x + .5,
- redraw_rect.y,
- redraw_rect.width - x - .5,
- redraw_rect.y + redraw_rect.height);
-
- cairo_set_source_rgba (cr,
- view->priv->right_margin_overlay_color->red / 65535.,
- view->priv->right_margin_overlay_color->green / 65535.,
- view->priv->right_margin_overlay_color->blue / 65535.,
- RIGHT_MARING_OVERLAY_ALPHA / 255.);
-
- cairo_fill (cr);
- }
+ if (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT))
+ gtk_source_view_paint_marks_background (view, event);
- cairo_destroy (cr);
+ /* Have GtkTextView draw the text first. */
+ if (GTK_WIDGET_CLASS (gtk_source_view_parent_class)->expose_event)
+ event_handled =
+ GTK_WIDGET_CLASS (gtk_source_view_parent_class)->expose_event (widget, event);
- PROFILE ({
- g_timer_stop (timer);
- g_message ("Time to draw the margin: %g (sec * 1000)",
- g_timer_elapsed (timer, NULL) * 1000);
- });
- }
+ /* Draw the right margin vertical line + overlay. */
+ if (view->priv->show_right_margin &&
+ (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)))
+ {
+ gtk_source_view_paint_right_margin (view, event);
+ }
- if (view->priv->draw_spaces != 0 &&
- (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)))
- {
- draw_tabs_and_spaces (view, event);
- }
+ if (view->priv->draw_spaces != 0 &&
+ (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)))
+ {
+ draw_tabs_and_spaces (view, event);
}
DEBUG ({
@@ -2280,42 +2417,33 @@ void
gtk_source_view_set_show_line_numbers (GtkSourceView *view,
gboolean show)
{
+ GtkSourceGutter *gutter;
+
g_return_if_fail (view != NULL);
g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
show = (show != FALSE);
- if (show)
+ if (show == view->priv->show_line_numbers)
{
- if (!view->priv->show_line_numbers)
- {
- /* Set left margin to minimum width if no margin is
- visible yet. Otherwise, just queue a redraw, so the
- expose handler will automatically adjust the margin. */
- if (!view->priv->show_line_marks)
- gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view),
- GTK_TEXT_WINDOW_LEFT,
- MIN_NUMBER_WINDOW_WIDTH);
- else
- gtk_widget_queue_draw (GTK_WIDGET (view));
+ return;
+ }
- view->priv->show_line_numbers = show;
+ gutter = gtk_source_view_get_gutter (view, GTK_TEXT_WINDOW_LEFT);
- g_object_notify (G_OBJECT (view), "show_line_numbers");
- }
+ if (show)
+ {
+ gtk_cell_renderer_set_fixed_size (view->priv->line_renderer, -1, -1);
}
else
{
- if (view->priv->show_line_numbers)
- {
- view->priv->show_line_numbers = show;
+ gtk_cell_renderer_set_fixed_size (view->priv->line_renderer, 0, 0);
+ }
- /* force expose event, which will adjust margin. */
- gtk_widget_queue_draw (GTK_WIDGET (view));
+ view->priv->show_line_numbers = show;
+ gtk_source_gutter_queue_draw (gutter);
- g_object_notify (G_OBJECT (view), "show_line_numbers");
- }
- }
+ g_object_notify (G_OBJECT (view), "show_line_numbers");
}
/**
@@ -2349,41 +2477,33 @@ void
gtk_source_view_set_show_line_marks (GtkSourceView *view,
gboolean show)
{
+ GtkSourceGutter *gutter;
+
g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
show = (show != FALSE);
+ gutter = gtk_source_view_get_gutter (view, GTK_TEXT_WINDOW_LEFT);
- if (show)
+ if (show == view->priv->show_line_marks)
{
- if (!view->priv->show_line_marks)
- {
- /* Set left margin to minimum width if no margin is
- visible yet. Otherwise, just queue a redraw, so the
- expose handler will automatically adjust the margin. */
- if (!view->priv->show_line_numbers)
- gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view),
- GTK_TEXT_WINDOW_LEFT,
- MIN_NUMBER_WINDOW_WIDTH);
- else
- gtk_widget_queue_draw (GTK_WIDGET (view));
+ return;
+ }
- view->priv->show_line_marks = show;
+ gutter = gtk_source_view_get_gutter (view, GTK_TEXT_WINDOW_LEFT);
- g_object_notify (G_OBJECT (view), "show-line-marks");
- }
+ if (show)
+ {
+ gtk_cell_renderer_set_fixed_size (view->priv->marks_renderer, GUTTER_PIXMAP, GUTTER_PIXMAP);
}
else
{
- if (view->priv->show_line_marks)
- {
- view->priv->show_line_marks = show;
+ gtk_cell_renderer_set_fixed_size (view->priv->marks_renderer, 0, 0);
+ }
- /* force expose event, which will adjust margin. */
- gtk_widget_queue_draw (GTK_WIDGET (view));
+ view->priv->show_line_marks = show;
+ gtk_source_gutter_queue_draw (gutter);
- g_object_notify (G_OBJECT (view), "show-line-marks");
- }
- }
+ g_object_notify (G_OBJECT (view), "show_line_marks");
}
static gboolean
@@ -2722,7 +2842,7 @@ set_mark_category_tooltip_func (GtkSourceView *view,
*
* Set a #GtkSourceViewMarkTooltipFunc used to set tooltip on marks from the
* given mark @category. If @func is %NULL and @markup_func is %NULL then tooltips
- * will not be shown for marks from @category. If @markup_func is not %NULL
+ * will not be shown for marks from @category. If @markup_func is not %NULL
* @markup_func is going to be used instead of @func.
*
* <informalexample><programlisting>
@@ -3468,236 +3588,6 @@ gtk_source_view_key_press_event (GtkWidget *widget,
return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->key_press_event (widget, event);
}
-static void
-extend_selection_to_line (GtkTextBuffer *buf, GtkTextIter *line_start)
-{
- GtkTextIter start;
- GtkTextIter end;
- GtkTextIter line_end;
-
- gtk_text_buffer_get_selection_bounds (buf, &start, &end);
-
- line_end = *line_start;
- gtk_text_iter_forward_to_line_end (&line_end);
-
- if (gtk_text_iter_compare (&start, line_start) < 0)
- {
- gtk_text_buffer_select_range (buf, &start, &line_end);
- }
- else if (gtk_text_iter_compare (&end, &line_end) < 0)
- {
- /* if the selection is in this line, extend
- * the selection to the whole line */
- gtk_text_buffer_select_range (buf, &line_end, line_start);
- }
- else
- {
- gtk_text_buffer_select_range (buf, &end, line_start);
- }
-}
-
-static void
-select_line (GtkTextBuffer *buf, GtkTextIter *line_start)
-{
- GtkTextIter iter;
-
- iter = *line_start;
-
- if (!gtk_text_iter_ends_line (&iter))
- gtk_text_iter_forward_to_line_end (&iter);
-
- /* Select the line, put the cursor at the end of the line */
- gtk_text_buffer_select_range (buf, &iter, line_start);
-}
-
-static gboolean
-gtk_source_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
-{
- GtkSourceView *view;
- GtkTextBuffer *buf;
- int y_buf;
- GtkTextIter line_start;
-
- view = GTK_SOURCE_VIEW (widget);
- buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
-
- if (view->priv->show_line_numbers &&
- (event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),
- GTK_TEXT_WINDOW_LEFT)))
- {
- gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
- GTK_TEXT_WINDOW_LEFT,
- event->x, event->y,
- NULL, &y_buf);
-
- gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view),
- &line_start,
- y_buf,
- NULL);
-
- if (event->type == GDK_BUTTON_PRESS && (event->button == 1))
- {
- if ((event->state & GDK_CONTROL_MASK) != 0)
- {
- /* Single click + Ctrl -> select the line */
- select_line (buf, &line_start);
- }
- else if ((event->state & GDK_SHIFT_MASK) != 0)
- {
- /* Single click + Shift -> extended current
- selection to include the clicked line */
- extend_selection_to_line (buf, &line_start);
- }
- else
- {
- gtk_text_buffer_place_cursor (buf, &line_start);
- }
- }
- else if (event->type == GDK_2BUTTON_PRESS && (event->button == 1))
- {
- select_line (buf, &line_start);
- }
-
- /* consume the event also on right click etc */
- return TRUE;
- }
-
- return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->button_press_event (widget, event);
-}
-
-static void
-set_tooltip_widget_from_marks (GtkSourceView *view,
- GtkTooltip *tooltip,
- GSList *marks)
-{
- GtkWidget *vbox;
-
- vbox = gtk_vbox_new (FALSE, 0);
- gtk_widget_show (vbox);
-
- while (marks != NULL)
- {
- MarkCategory *cat;
- GtkSourceMark *mark;
-
- mark = marks->data;
- cat = gtk_source_view_get_mark_category (view, mark);
-
- if (cat != NULL)
- {
- GtkWidget *image;
- GtkWidget *label;
- GtkWidget *hbox;
- GtkWidget *separator;
- gchar *text;
-
- hbox = gtk_hbox_new (FALSE, 4);
- gtk_widget_show (hbox);
- gtk_box_pack_start (GTK_BOX (vbox), hbox,
- FALSE, FALSE, 0);
-
- text = cat->tooltip_func (mark, cat->tooltip_data);
-
- if (text != NULL)
- {
- label = gtk_label_new (NULL);
- if (cat->tooltip_markup)
- gtk_label_set_markup (GTK_LABEL (label), text);
- else
- gtk_label_set_text (GTK_LABEL (label), text);
-
- gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
- gtk_widget_show (label);
-
- gtk_box_pack_end (GTK_BOX (hbox), label,
- TRUE, TRUE, 0);
-
- if (cat->pixbuf != NULL)
- {
- image = gtk_image_new_from_pixbuf (cat->pixbuf);
- gtk_widget_show (image);
-
- gtk_box_pack_start (GTK_BOX (hbox), image,
- FALSE, FALSE, 0);
- }
-
- if (g_slist_length (marks) != 1)
- {
- separator = gtk_hseparator_new ();
- gtk_widget_show (separator);
- gtk_box_pack_start (GTK_BOX (vbox), separator,
- FALSE, FALSE, 0);
- }
-
- g_free (text);
- }
- }
-
- marks = g_slist_delete_link (marks, marks);
- }
-
- gtk_tooltip_set_custom (tooltip, vbox);
-}
-
-static gboolean
-gtk_source_view_query_tooltip (GtkWidget *widget,
- gint x,
- gint y,
- gboolean keyboard_mode,
- GtkTooltip *tooltip)
-{
- GtkTextView *text_view = GTK_TEXT_VIEW (widget);
- GtkSourceView *view = GTK_SOURCE_VIEW (widget);
-
- /* Check if we are inside the marker area */
- /* TODO: when folding is in, correct this */
- if (x < gtk_text_view_get_border_window_size (text_view, GTK_TEXT_WINDOW_LEFT) &&
- x > view->priv->cached_line_number_width)
- {
- GtkTextIter line_iter;
- GSList *marks;
- GtkSourceBuffer *buffer;
- gint line;
- gint buffer_x, buffer_y;
-
- buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (text_view));
- gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (widget),
- GTK_TEXT_WINDOW_LEFT,
- x,
- y,
- &buffer_x,
- &buffer_y);
-
- gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (widget),
- &line_iter,
- buffer_y,
- NULL);
- line = gtk_text_iter_get_line (&line_iter);
- marks = gtk_source_buffer_get_source_marks_at_line (buffer,
- line,
- NULL);
-
- if (marks != NULL)
- {
- marks = g_slist_sort_with_data (marks, sort_marks_by_priority, view);
- marks = g_slist_reverse (marks);
-
- set_tooltip_widget_from_marks (view, tooltip, marks);
-
- return TRUE;
- }
- }
-
- if (GTK_WIDGET_CLASS (gtk_source_view_parent_class)->query_tooltip != NULL)
- return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->query_tooltip (widget,
- x,
- y,
- keyboard_mode,
- tooltip);
- else
- return FALSE;
-}
-
/**
* gtk_source_view_get_auto_indent:
* @view: a #GtkSourceView.
@@ -4080,7 +3970,7 @@ gtk_source_view_set_draw_spaces (GtkSourceView *view,
*
* Returns the #GtkSourceDrawSpacesFlags specifying if and how spaces
* should be displayed for this @view.
- *
+ *
* Returns: the #GtkSourceDrawSpacesFlags, 0 if no spaces should be drawn.
**/
GtkSourceDrawSpacesFlags
@@ -4286,3 +4176,49 @@ gtk_source_view_update_style_scheme (GtkSourceView *view)
view->priv->style_scheme_applied = FALSE;
}
}
+
+/**
+ * gtk_source_view_get_gutter:
+ * @view: a #GtkSourceView
+ * @window_type: the gutter window type
+ *
+ * Returns the #GtkSourceGutter object associated with @window_type for @view.
+ * Only GTK_TEXT_WINDOW_LEFT and GTK_TEXT_WINDOW_RIGHT are supported,
+ * respectively corresponding to the left and right gutter. The line numbers
+ * and mark category icons are rendered in the gutter corresponding to
+ * GTK_TEXT_WINDOW_LEFT.
+ *
+ * Since: 2.8
+ *
+ * Returns: the #GtkSourceGutter.
+ **/
+GtkSourceGutter *
+gtk_source_view_get_gutter (GtkSourceView *view,
+ GtkTextWindowType window_type)
+{
+ g_return_val_if_fail (GTK_IS_SOURCE_VIEW (view), NULL);
+ g_return_val_if_fail (window_type == GTK_TEXT_WINDOW_LEFT ||
+ window_type == GTK_TEXT_WINDOW_RIGHT, NULL);
+
+ if (window_type == GTK_TEXT_WINDOW_LEFT)
+ {
+ if (view->priv->left_gutter == NULL)
+ {
+ view->priv->left_gutter = gtk_source_gutter_new (view,
+ window_type);
+ }
+
+ return view->priv->left_gutter;
+ }
+ else
+ {
+ if (view->priv->right_gutter == NULL)
+ {
+ view->priv->right_gutter = gtk_source_gutter_new (view,
+ window_type);
+ }
+
+ return view->priv->right_gutter;
+ }
+}
+
diff --git a/gtksourceview/gtksourceview.h b/gtksourceview/gtksourceview.h
index 3eae9af..774edb8 100644
--- a/gtksourceview/gtksourceview.h
+++ b/gtksourceview/gtksourceview.h
@@ -28,6 +28,7 @@
#include <gtk/gtktextview.h>
#include <gtksourceview/gtksourcebuffer.h>
+#include <gtksourceview/gtksourcegutter.h>
G_BEGIN_DECLS
@@ -38,6 +39,11 @@ G_BEGIN_DECLS
#define GTK_IS_SOURCE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_VIEW))
#define GTK_SOURCE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_VIEW, GtkSourceViewClass))
+typedef enum
+{
+ GTK_SOURCE_VIEW_GUTTER_POSITION_LINES = -30,
+ GTK_SOURCE_VIEW_GUTTER_POSITION_MARKS = -20
+} GtkSourceViewGutterPosition;
typedef struct _GtkSourceView GtkSourceView;
typedef struct _GtkSourceViewClass GtkSourceViewClass;
@@ -57,12 +63,14 @@ struct _GtkSourceViewClass
void (*undo) (GtkSourceView *view);
void (*redo) (GtkSourceView *view);
+ void (*line_mark_activated) (GtkSourceView *view,
+ GtkTextIter *iter,
+ GdkEvent *event);
/* Padding for future expansion */
void (*_gtk_source_reserved1) (void);
void (*_gtk_source_reserved2) (void);
void (*_gtk_source_reserved3) (void);
- void (*_gtk_source_reserved4) (void);
};
/**
@@ -213,5 +221,8 @@ void gtk_source_view_set_draw_spaces (GtkSourceView *view,
GtkSourceDrawSpacesFlags
gtk_source_view_get_draw_spaces (GtkSourceView *view);
+GtkSourceGutter *gtk_source_view_get_gutter (GtkSourceView *view,
+ GtkTextWindowType window_type);
+
G_END_DECLS
#endif /* end of SOURCE_VIEW_H__ */
diff --git a/tests/test-widget.c b/tests/test-widget.c
index 68adf87..e9bdc96 100644
--- a/tests/test-widget.c
+++ b/tests/test-widget.c
@@ -1112,69 +1112,44 @@ window_deleted_cb (GtkWidget *widget, GdkEvent *ev, gpointer user_data)
return TRUE;
}
-static gboolean
-button_press_cb (GtkWidget *widget, GdkEventButton *ev, gpointer user_data)
+static void
+line_mark_activated (GtkSourceGutter *gutter,
+ GtkTextIter *iter,
+ GdkEventButton *ev,
+ GtkSourceView *view)
{
- GtkSourceView *view;
GtkSourceBuffer *buffer;
+ GSList *mark_list;
+ const gchar *mark_type;
- g_return_val_if_fail (GTK_IS_SOURCE_VIEW (widget), FALSE);
+ if (ev->button == 1)
+ mark_type = MARK_TYPE_1;
+ else
+ mark_type = MARK_TYPE_2;
- view = GTK_SOURCE_VIEW (widget);
buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
- if (!gtk_source_view_get_show_line_marks (view))
- return FALSE;
+ /* get the marks already in the line */
+ mark_list = gtk_source_buffer_get_source_marks_at_line (buffer,
+ gtk_text_iter_get_line (iter),
+ mark_type);
- /* check that the click was on the left gutter */
- if (ev->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),
- GTK_TEXT_WINDOW_LEFT))
+ if (mark_list != NULL)
{
- gint y_buf;
- GtkTextIter line_start;
- GSList *mark_list;
- const gchar *mark_type;
-
- if (ev->button == 1)
- mark_type = MARK_TYPE_1;
- else
- mark_type = MARK_TYPE_2;
-
- gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
- GTK_TEXT_WINDOW_LEFT,
- ev->x, ev->y,
- NULL, &y_buf);
-
- /* get line bounds */
- gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view),
- &line_start,
- y_buf,
- NULL);
-
- /* get the marks already in the line */
- mark_list = gtk_source_buffer_get_source_marks_at_line (buffer,
- gtk_text_iter_get_line (&line_start),
- mark_type);
-
- if (mark_list != NULL)
- {
- /* just take the first and delete it */
- gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer),
- GTK_TEXT_MARK (mark_list->data));
- }
- else
- {
- /* no mark found: create one */
- gtk_source_buffer_create_source_mark (buffer,
- NULL,
- mark_type,
- &line_start);
- }
-
- g_slist_free (mark_list);
+ /* just take the first and delete it */
+ gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer),
+ GTK_TEXT_MARK (mark_list->data));
+ }
+ else
+ {
+ /* no mark found: create one */
+ gtk_source_buffer_create_source_mark (buffer,
+ NULL,
+ mark_type,
+ iter);
}
- return FALSE;
+ g_slist_free (mark_list);
}
@@ -1222,13 +1197,13 @@ create_view_window (GtkSourceBuffer *buffer, GtkSourceView *from)
/* view */
view = gtk_source_view_new_with_buffer (buffer);
-
+
if (style_scheme)
gtk_source_buffer_set_style_scheme (buffer, style_scheme);
g_signal_connect (buffer, "mark-set", G_CALLBACK (move_cursor_cb), view);
g_signal_connect (buffer, "changed", G_CALLBACK (update_cursor_position), view);
- g_signal_connect (view, "button-press-event", G_CALLBACK (button_press_cb), NULL);
+ g_signal_connect (view, "line-mark-activated", G_CALLBACK (line_mark_activated), view);
g_signal_connect (window, "delete-event", (GCallback) window_deleted_cb, view);
/* action group and UI manager */
@@ -1289,7 +1264,7 @@ create_view_window (GtkSourceBuffer *buffer, GtkSourceView *from)
gtk_widget_modify_font (view, font_desc);
pango_font_description_free (font_desc);
}
-
+
/* change view attributes to match those of from */
if (from)
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]