418 lines
11 KiB
C
418 lines
11 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
|
|
* gtksourcegutterrenderermarks.c
|
|
* This file is part of GtkSourceView
|
|
*
|
|
* Copyright (C) 2010 - Jesse van den Kieboom
|
|
*
|
|
* GtkSourceView is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* GtkSourceView is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "gtksourcegutterrenderermarks.h"
|
|
#include "gtksourceview.h"
|
|
#include "gtksourcebuffer.h"
|
|
#include "gtksourcemarkattributes.h"
|
|
#include "gtksourcemark.h"
|
|
|
|
#define COMPOSITE_ALPHA 225
|
|
|
|
G_DEFINE_TYPE (GtkSourceGutterRendererMarks, gtk_source_gutter_renderer_marks, GTK_SOURCE_TYPE_GUTTER_RENDERER_PIXBUF)
|
|
|
|
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)
|
|
{
|
|
gint priority1 = -1;
|
|
gint priority2 = -1;
|
|
|
|
gtk_source_view_get_mark_attributes (view,
|
|
gtk_source_mark_get_category (mark1),
|
|
&priority1);
|
|
|
|
gtk_source_view_get_mark_attributes (view,
|
|
gtk_source_mark_get_category (mark2),
|
|
&priority2);
|
|
|
|
return priority1 - priority2;
|
|
}
|
|
else
|
|
{
|
|
return line2 - line1;
|
|
}
|
|
}
|
|
|
|
static int
|
|
measure_line_height (GtkSourceView *view)
|
|
{
|
|
PangoLayout *layout;
|
|
gint height = 12;
|
|
|
|
layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), "QWERTY");
|
|
|
|
if (layout)
|
|
{
|
|
pango_layout_get_pixel_size (layout, NULL, &height);
|
|
g_object_unref (layout);
|
|
}
|
|
|
|
return height - 2;
|
|
}
|
|
|
|
static GdkPixbuf *
|
|
composite_marks (GtkSourceView *view,
|
|
GSList *marks,
|
|
gint size)
|
|
{
|
|
GdkPixbuf *composite;
|
|
gint mark_width;
|
|
gint mark_height;
|
|
|
|
/* Draw the mark with higher priority */
|
|
marks = g_slist_sort_with_data (marks, sort_marks_by_priority, view);
|
|
|
|
composite = NULL;
|
|
mark_width = 0;
|
|
mark_height = 0;
|
|
|
|
/* composite all the pixbufs for the marks present at the line */
|
|
do
|
|
{
|
|
GtkSourceMark *mark;
|
|
GtkSourceMarkAttributes *attrs;
|
|
const GdkPixbuf *pixbuf;
|
|
|
|
mark = marks->data;
|
|
attrs = gtk_source_view_get_mark_attributes (view,
|
|
gtk_source_mark_get_category (mark),
|
|
NULL);
|
|
|
|
if (attrs == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pixbuf = gtk_source_mark_attributes_render_icon (attrs,
|
|
GTK_WIDGET (view),
|
|
size);
|
|
|
|
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,
|
|
(gdouble) pixbuf_w / mark_width,
|
|
(gdouble) pixbuf_h / mark_height,
|
|
GDK_INTERP_BILINEAR,
|
|
COMPOSITE_ALPHA);
|
|
}
|
|
}
|
|
|
|
marks = g_slist_next (marks);
|
|
}
|
|
while (marks);
|
|
|
|
return composite;
|
|
}
|
|
|
|
static void
|
|
gutter_renderer_query_data (GtkSourceGutterRenderer *renderer,
|
|
GtkTextIter *start,
|
|
GtkTextIter *end,
|
|
GtkSourceGutterRendererState state)
|
|
{
|
|
GSList *marks;
|
|
GdkPixbuf *pixbuf = NULL;
|
|
gint size = 0;
|
|
GtkSourceView *view;
|
|
GtkSourceBuffer *buffer;
|
|
|
|
view = GTK_SOURCE_VIEW (gtk_source_gutter_renderer_get_view (renderer));
|
|
buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
|
|
|
|
marks = gtk_source_buffer_get_source_marks_at_iter (buffer,
|
|
start,
|
|
NULL);
|
|
|
|
if (marks != NULL)
|
|
{
|
|
size = measure_line_height (view);
|
|
pixbuf = composite_marks (view, marks, size);
|
|
|
|
g_slist_free (marks);
|
|
}
|
|
|
|
g_object_set (G_OBJECT (renderer),
|
|
"pixbuf", pixbuf,
|
|
"xpad", 2,
|
|
"yalign", 0.5,
|
|
"xalign", 0.5,
|
|
"alignment-mode", GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_FIRST,
|
|
NULL);
|
|
|
|
g_clear_object (&pixbuf);
|
|
}
|
|
|
|
static gboolean
|
|
set_tooltip_widget_from_marks (GtkSourceView *view,
|
|
GtkTooltip *tooltip,
|
|
GSList *marks)
|
|
{
|
|
GtkGrid *grid = NULL;
|
|
gint row_num = 0;
|
|
gint icon_size;
|
|
|
|
gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &icon_size);
|
|
|
|
for (; marks; marks = g_slist_next (marks))
|
|
{
|
|
const gchar *category;
|
|
GtkSourceMark *mark;
|
|
GtkSourceMarkAttributes *attrs;
|
|
gchar *text;
|
|
gboolean ismarkup = FALSE;
|
|
GtkWidget *label;
|
|
const GdkPixbuf *pixbuf;
|
|
|
|
mark = marks->data;
|
|
category = gtk_source_mark_get_category (mark);
|
|
|
|
attrs = gtk_source_view_get_mark_attributes (view, category, NULL);
|
|
|
|
if (attrs == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
text = gtk_source_mark_attributes_get_tooltip_markup (attrs, mark);
|
|
|
|
if (text == NULL)
|
|
{
|
|
text = gtk_source_mark_attributes_get_tooltip_text (attrs, mark);
|
|
}
|
|
else
|
|
{
|
|
ismarkup = TRUE;
|
|
}
|
|
|
|
if (text == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (grid == NULL)
|
|
{
|
|
grid = GTK_GRID (gtk_grid_new ());
|
|
gtk_grid_set_column_spacing (grid, 4);
|
|
gtk_widget_show (GTK_WIDGET (grid));
|
|
}
|
|
|
|
label = gtk_label_new (NULL);
|
|
|
|
if (ismarkup)
|
|
{
|
|
gtk_label_set_markup (GTK_LABEL (label), text);
|
|
}
|
|
else
|
|
{
|
|
gtk_label_set_text (GTK_LABEL (label), text);
|
|
}
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
gtk_widget_set_valign (label, GTK_ALIGN_START);
|
|
gtk_widget_show (label);
|
|
|
|
pixbuf = gtk_source_mark_attributes_render_icon (attrs,
|
|
GTK_WIDGET (view),
|
|
icon_size);
|
|
|
|
if (pixbuf == NULL)
|
|
{
|
|
gtk_grid_attach (grid, label, 0, row_num, 2, 1);
|
|
}
|
|
else
|
|
{
|
|
GtkWidget *image;
|
|
GdkPixbuf *copy;
|
|
|
|
/* FIXME why a copy is needed? */
|
|
copy = gdk_pixbuf_copy (pixbuf);
|
|
image = gtk_image_new_from_pixbuf (copy);
|
|
g_object_unref (copy);
|
|
|
|
gtk_widget_set_halign (image, GTK_ALIGN_START);
|
|
gtk_widget_set_valign (image, GTK_ALIGN_START);
|
|
gtk_widget_show (image);
|
|
|
|
gtk_grid_attach (grid, image, 0, row_num, 1, 1);
|
|
gtk_grid_attach (grid, label, 1, row_num, 1, 1);
|
|
}
|
|
|
|
row_num++;
|
|
|
|
if (marks->next != NULL)
|
|
{
|
|
GtkWidget *separator;
|
|
|
|
separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
|
|
|
|
gtk_widget_show (separator);
|
|
|
|
gtk_grid_attach (grid, separator, 0, row_num, 2, 1);
|
|
row_num++;
|
|
}
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
if (grid == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
gtk_tooltip_set_custom (tooltip, GTK_WIDGET (grid));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gutter_renderer_query_tooltip (GtkSourceGutterRenderer *renderer,
|
|
GtkTextIter *iter,
|
|
GdkRectangle *area,
|
|
gint x,
|
|
gint y,
|
|
GtkTooltip *tooltip)
|
|
{
|
|
GSList *marks;
|
|
GtkSourceView *view;
|
|
GtkSourceBuffer *buffer;
|
|
gboolean ret;
|
|
|
|
view = GTK_SOURCE_VIEW (gtk_source_gutter_renderer_get_view (renderer));
|
|
buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
|
|
|
|
marks = gtk_source_buffer_get_source_marks_at_iter (buffer,
|
|
iter,
|
|
NULL);
|
|
|
|
if (marks != NULL)
|
|
{
|
|
marks = g_slist_sort_with_data (marks,
|
|
sort_marks_by_priority,
|
|
view);
|
|
|
|
marks = g_slist_reverse (marks);
|
|
|
|
ret = set_tooltip_widget_from_marks (view, tooltip, marks);
|
|
|
|
g_slist_free (marks);
|
|
|
|
return ret;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gutter_renderer_query_activatable (GtkSourceGutterRenderer *renderer,
|
|
GtkTextIter *iter,
|
|
GdkRectangle *area,
|
|
GdkEvent *event)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gutter_renderer_change_view (GtkSourceGutterRenderer *renderer,
|
|
GtkTextView *old_view)
|
|
{
|
|
GtkSourceView *view;
|
|
|
|
view = GTK_SOURCE_VIEW (gtk_source_gutter_renderer_get_view (renderer));
|
|
|
|
if (view != NULL)
|
|
{
|
|
gtk_source_gutter_renderer_set_size (renderer,
|
|
measure_line_height (view));
|
|
}
|
|
|
|
if (GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_marks_parent_class)->change_view != NULL)
|
|
{
|
|
GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_marks_parent_class)->change_view (renderer,
|
|
old_view);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_source_gutter_renderer_marks_class_init (GtkSourceGutterRendererMarksClass *klass)
|
|
{
|
|
GtkSourceGutterRendererClass *renderer_class = GTK_SOURCE_GUTTER_RENDERER_CLASS (klass);
|
|
|
|
renderer_class->query_data = gutter_renderer_query_data;
|
|
renderer_class->query_tooltip = gutter_renderer_query_tooltip;
|
|
renderer_class->query_activatable = gutter_renderer_query_activatable;
|
|
renderer_class->change_view = gutter_renderer_change_view;
|
|
}
|
|
|
|
static void
|
|
gtk_source_gutter_renderer_marks_init (GtkSourceGutterRendererMarks *self)
|
|
{
|
|
}
|
|
|
|
GtkSourceGutterRenderer *
|
|
gtk_source_gutter_renderer_marks_new (void)
|
|
{
|
|
return g_object_new (GTK_SOURCE_TYPE_GUTTER_RENDERER_MARKS, NULL);
|
|
}
|